From f5d515b0f9d94ec9bc79028be80c34b64be2e153 Mon Sep 17 00:00:00 2001 From: sangge-win <2251250136@qq.com> Date: Tue, 9 Apr 2024 14:34:58 +0800 Subject: [PATCH] add init code --- CoreAlgorithm.py | 516 +++++++++++ DataSearch.py | 295 ++++++ README.md | 3 + background.png | Bin 0 -> 175359 bytes data_provider.py | 471 ++++++++++ data_requirer.py | 508 +++++++++++ encoding.py | 162 ++++ note.txt | 10 + requirements.txt | 2 + server.py | 562 ++++++++++++ server_demo.py | 262 ++++++ util.py | 2286 ++++++++++++++++++++++++++++++++++++++++++++++ 12 files changed, 5077 insertions(+) create mode 100644 CoreAlgorithm.py create mode 100644 DataSearch.py create mode 100644 README.md create mode 100644 background.png create mode 100644 data_provider.py create mode 100644 data_requirer.py create mode 100644 encoding.py create mode 100644 note.txt create mode 100644 requirements.txt create mode 100644 server.py create mode 100644 server_demo.py create mode 100644 util.py diff --git a/CoreAlgorithm.py b/CoreAlgorithm.py new file mode 100644 index 0000000..ecf57a7 --- /dev/null +++ b/CoreAlgorithm.py @@ -0,0 +1,516 @@ +import random +from collections.abc import Mapping +from encoding import EncodedNumber +from util import invert, powmod, getprimeover + +DEFAULT_KEYSIZE = 1024 # 定义默认的二进制数长度 + + +def generate_paillier_keypair( + private_keyring=None, n_length=DEFAULT_KEYSIZE +): # 生成公钥和私钥的函数 + # 生成 Paillier 密钥对函数 + p = q = n = None # 初始化素数 p, q 和计算结果 n + length = 0 # 初始化计算结果 n 的长度 (即用二进制表示 n 所需要的二进制位数) + + while length != n_length: # 循环直至计算结果 n 的长度达到指定长度 n_length + p = getprimeover(n_length // 2) # 随机生成一个 (n_length//2) 长的素数 p + q = p + while q == p: + # 确保 q 与 p 不相等 + q = getprimeover(n_length // 2) # 随机生成一个 (n_length//2) 长的素数 q + n = p * q # 计算 n,即两个素数乘积 + length = n.bit_length() # 计算 n 的二进制长度 + + # 创建公钥对象 + public_key = PaillierPublicKey(n) + # 创建私钥对象 + private_key = PaillierPrivateKey(public_key, p, q) + + if private_keyring is not None: # 如果传入了私钥环对象,则将私钥添加到私钥环中 + private_keyring.add(private_key) + + return public_key, private_key # 返回公钥和私钥 + + +class PaillierPublicKey(object): # 定义公钥类 + def __init__(self, n): + self.g = n + 1 + self.n = n # 公钥的模数 + self.nsquare = n * n # n的平方 + self.max_int = n // 3 - 1 # 公钥的一个属性(限制可加密/解密的最大整数值) + + def __repr__(self): # 用于打印出该类的对象 + public_key_hash = hex(hash(self))[2:] + return "".format(public_key_hash[:10]) # 返回表示对象的字符串 + + def __eq__(self, other): # 用于比较两个对象是否相等,并返回比较结果 + return self.n == other.n + + def __hash__(self): # 用于返回n的Hash值 + return hash(self.n) + + def get_n_and_g(self): # 获取该公钥的 n 和 g 的值 + return self.n, self.g + + def raw_encrypt(self, plaintext, r_value=None): # 用于返回加密后的密文,其中r_value可给随机数赋值 + if not isinstance(plaintext, int): # 判断plaintext是否是整数 + raise TypeError("明文不是整数,而是: %s" % type(plaintext)) + + if self.n - self.max_int <= plaintext < self.n: # 对于非常大的明文,使用特殊的计算方法进行加密: + neg_plaintext = self.n - plaintext # = abs(plaintext - nsquare) + neg_ciphertext = (self.n * neg_plaintext + 1) % self.nsquare + nude_ciphertext = invert(neg_ciphertext, self.nsquare) + else: # 如果不是非常大的明文: + nude_ciphertext = ( + self.n * plaintext + 1 + ) % self.nsquare # (n + 1)^plaintext = n * plaintext + 1 mod n^2 + + # 生成一个随机数,其值为r_value。如果r_value没有值,则r随机: + r = r_value or self.get_random_lt_n() + + obfuscator = powmod(r, self.n, self.nsquare) # (r ^ n) mod n^2 + return (nude_ciphertext * obfuscator) % self.nsquare # 返回加密后的密文 + + def get_random_lt_n(self): # 返回一个1——n间的随机整数 + return random.SystemRandom().randrange(1, self.n) + + def encrypt( + self, value, precision=None, r_value=None + ): # value表示要加密的值,precision是加密精度,r_value是随机数 + # 判断value是否是EncodedNumber类型,如果是则直接赋值给encoding;如果不是,则对value进行编码 + if isinstance(value, EncodedNumber): + encoding = value + else: + encoding = EncodedNumber.encode(self, value, precision) + return self.encrypt_encoded(encoding, r_value) + + def encrypt_encoded(self, encoding, r_value): # 将已编码的数值对象转换为加密后的数值对象,并可以选择进行混淆处理 + obfuscator = r_value or 1 # 为随机数r_value,没有则默认为1 + ciphertext = self.raw_encrypt(encoding.encoding, r_value=obfuscator) + encrypted_number = EncryptedNumber(self, ciphertext, encoding.exponent) + + """ + PS:默认生成情况下(不输入随机数r_value的情况下): + encrypt中的随机数r_value为:None + raw_encrypt中的随机数为:1 + encrypt_encoded中的随机数为:None + """ + + if r_value is None: # 结合上述注释,可知:密文混淆函数是会默认执行的 + encrypted_number.obfuscate() # 如果encrypt_encoded没有随机数r_value,则进行密文混淆处理obfuscate() + + return encrypted_number + + +class PaillierPrivateKey(object): # 私钥 + def __init__(self, public_key, p, q): + if not p * q == public_key.n: # 如果p * q 不等于 公钥的n,则说明出错 + raise ValueError("所给公钥与p,q不匹配!") + if p == q: # p,q相同 + raise ValueError("p,q不能相同!") + self.public_key = public_key + + # 给self的p q赋值: + if q < p: # 默认是p 大于等于 q + self.p = q + self.q = p + else: + self.p = p + self.q = q + self.psquare = self.p * self.p + self.qsquare = self.q * self.q + self.p_inverse = invert(self.p, self.q) # 计算p mod q 的乘法逆元 + + self.hp = self.h_function(self.p, self.psquare) # p mod p方 + self.hq = self.h_function(self.q, self.qsquare) # q mod q方 + + def __repr__(self): # 用于打印出该类的对象 + pub_repr = repr(self.public_key) + return "".format(pub_repr) + + def decrypt(self, encrypted_number): # 解密密文,并返回明文 + # 执行下面这个语句前的类型为EncryptedNumber,执行完毕后类型为EncodedNumber(中间会变为int型的ciphertext): + encoded = self.decrypt_encoded(encrypted_number) + return encoded.decode() + + def decrypt_encoded( + self, encrypted_number, Encoding=None + ): # 用于解密密文并返回解密后的EncodedNumber类型 + # 检查输入信息是否是EncryptedNumber参数,如果不是: + if not isinstance(encrypted_number, EncryptedNumber): + raise TypeError( + "参数应该是EncryptedNumber," " 参数不能为: %s" % type(encrypted_number) + ) + + if self.public_key != encrypted_number.public_key: # 如果公钥与加密数字的公钥不一致 + raise ValueError("加密信息不能被不同的公钥进行加密!") + + if Encoding is None: # 将Encoding设置为未赋值的EncodedNumber变量 + Encoding = EncodedNumber + + """提取 encrypted_number 中的 ciphertext + 这里是禁用安全模式, + 所以是直接提取ciphertext, + 随后调用raw_decrypt函数对ciphertext进行处理:""" + encoded = self.raw_decrypt(encrypted_number.ciphertext(be_secure=False)) + + return Encoding(self.public_key, encoded, encrypted_number.exponent) + + def raw_decrypt(self, ciphertext): # 对密文进行原始解密 + if not isinstance(ciphertext, int): # 如果所给的密文不是int型 + raise TypeError("密文应该是int型, 而不是: %s" % type(ciphertext)) + + # 将解密结果存放在p和q中,并将p q进行合并: + decrypt_to_p = ( + self.l_function(powmod(ciphertext, self.p - 1, self.psquare), self.p) + * self.hp + % self.p + ) + decrypt_to_q = ( + self.l_function(powmod(ciphertext, self.q - 1, self.qsquare), self.q) + * self.hq + % self.q + ) + return self.crt(decrypt_to_p, decrypt_to_q) + + def h_function(self, x, xsquare): # 计算并返回h函数值[用于中国剩余定理] + return invert(self.l_function(powmod(self.public_key.g, x - 1, xsquare), x), x) + + def l_function(self, mju, p): # 计算并返回l值(算L(μ) ) + return (mju - 1) // p + + def crt(self, mp, mq): # 实现中国剩余定理(Chinese remainder theorem) + u = (mq - mp) * self.p_inverse % self.q + return mp + (u * self.p) + + def __eq__(self, other): # 判断两个对象的 q 与 p 是否相等 + return self.p == other.p and self.q == other.q + + def __hash__(self): # 计算 p 与 q 元组的哈希值 + return hash((self.p, self.q)) + + +class PaillierPrivateKeyring(Mapping): # 私钥环类,并继承了Mapping类 + def __init__(self, private_keys=None): # 初始化私钥环对象(私钥环列表) + if private_keys is None: + private_keys = [] + + # 将私钥和公钥进行组合,并存储在私钥环中: + public_keys = [k.public_key for k in private_keys] + self.__keyring = dict(zip(public_keys, private_keys)) + + def __getitem__(self, key): # 通过公钥,来查找私钥环中对应的私钥 + return self.__keyring[key] + + def __len__(self): # 存储的私钥数量 + return len(self.__keyring) + + def __iter__(self): # 遍历私钥环中的公钥 + return iter(self.__keyring) + + def __delitem__(self, public_key): # 删除与公钥对应的私钥 + del self.__keyring[public_key] + + def add(self, private_key): # 向私钥环中添加私钥 + if not isinstance(private_key, PaillierPrivateKey): # 对要添加的私钥进行判断 + raise TypeError("私钥应该是PaillierPrivateKey类型, " "而不是 %s" % type(private_key)) + self.__keyring[private_key.public_key] = private_key # 将该公钥和对用的私钥一块儿加入到私钥环中 + + def decrypt(self, encrypted_number): # 对密文进行解密 + relevant_private_key = self.__keyring[ + encrypted_number.public_key + ] # 在私钥环中获取对应的私钥 + return relevant_private_key.decrypt(encrypted_number) # 返回加密结果 + + +class EncryptedNumber(object): # 浮点数或整数的Pailier加密 + """ + 1. D(E(a) * E(b)) = a + b + 2. D(E(a)**b) = a * b + """ + + def __init__(self, public_key, ciphertext, exponent=0): + self.public_key = public_key + self.__ciphertext = ciphertext # 密文 + self.exponent = exponent # 用于表示指数 + self.__is_obfuscated = False # 用于表示数据是否被混淆 + if isinstance(self.ciphertext, EncryptedNumber): # 如果密文是EncryptedNumber + raise TypeError("密文必须是int型") + if not isinstance( + self.public_key, PaillierPublicKey + ): # 如果公钥不是PaillierPublicKey + raise TypeError("公钥必须是PaillierPublicKey") + + def __add__(self, other): # 运算符重载,重载为EncryptedNumber与(EncryptedNumber/整数/浮点数)的加法 + if isinstance(other, EncryptedNumber): + return self._add_encrypted(other) + elif isinstance(other, EncodedNumber): + return self._add_encoded(other) + else: + return self._add_scalar(other) + + def __radd__(self, other): # 反加,处理整数/浮点数与EncryptedNumber之间的加法 + return self.__add__(other) + + def __mul__(self, other): # 运算符重载,重载为EncryptedNumber与(整数/浮点数)的乘法 + # 判断other对象是否是EncryptedNumber,如果是: + if isinstance(other, EncryptedNumber): + raise NotImplementedError("EncryptedNumber 与 EncryptedNumber 之间不能相乘!") + + if isinstance(other, EncodedNumber): + encoding = other + else: + encoding = EncodedNumber.encode(self.public_key, other) + product = self._raw_mul(encoding.encoding) # 重新更新乘积 + exponent = self.exponent + encoding.exponent # 重新更新指数 + return EncryptedNumber(self.public_key, product, exponent) + + def __rmul__(self, other): # 反乘,处理整数/浮点数与EncryptedNumber之间的乘法 + return self.__mul__(other) + + def __sub__(self, other): # 运算符重载,重载为EncryptedNumber与(EncryptedNumber/整数/浮点数)的减法 + return self + (other * -1) + + def __rsub__(self, other): # 处理整数/浮点数与EncryptedNumber之间的减法 + return other + (self * -1) + + def __truediv__( + self, scalar + ): # 运算符重载,重载为EncryptedNumber与(EncryptedNumber/整数/浮点数)的除法 + return self.__mul__(1 / scalar) + + def __invert__(self): # 运算符重载~(对 数 的取反) + return self * (-1) + + # def __pow__(self, exponent): # 运算符重载 ** (对密文的幂函数) + # if not isinstance(exponent, int): # 如果输入有问题 + # print("指数应输入 整数 标量!") + # else: + # result = self + # for i in [1, exponent]: + # result *= self + # return result + # # 原本的幂运算 ** ;return self.value ** exponent + + def ciphertext(self, be_secure=True): # 用于混淆密文,并返回混淆后的密文 + """ + EncryptedNumber类的一个方法ciphertext,用于返回该对象的密文。 + 在Paillier加密中,为了提高计算性能,加法和乘法操作进行了简化, + 避免对每个加法和乘法结果进行随机数的加密操作。 + 这样会使得内部计算快速,但会暴露一部分信息。 + 此外,为了保证安全,如果需要与其他人共享密文,应该使用be_secure=True。 + 这样,如果密文还没有被混淆,会调用obfuscate方法对其进行混淆操作。 + """ + if be_secure and not self.__is_obfuscated: # 如果密文没有被混淆,则进行混淆操作 + self.obfuscate() + return self.__ciphertext + + def decrease_exponent_to( + self, new_exp + ): # 返回一个指数较低但大小相同的数(即返回一个同值的,但指数较低的EncryptedNumber) + if new_exp > self.exponent: + raise ValueError("新指数值 %i 应比原指数 %i 小! " % (new_exp, self.exponent)) + + multiplied = self * pow(EncodedNumber.BASE, self.exponent - new_exp) # 降指数后的乘积 + multiplied.exponent = new_exp # 降指数后的新指数 + return multiplied + + def obfuscate(self): # 混淆密文 + r = self.public_key.get_random_lt_n() # 生成一个(1——n)间的随机数r,不 >= r + r_pow_n = powmod( + r, self.public_key.n, self.public_key.nsquare + ) # (r ^ n) mod n^2 + self.__ciphertext = ( + self.__ciphertext * r_pow_n % self.public_key.nsquare + ) # 对原密文进行处理 + self.__is_obfuscated = True # 用于判断密文是否被混淆 + + def _add_scalar(self, scalar): # 执行EncodedNumber与标量(整型/浮点型)相加的操作 + encoded = EncodedNumber.encode( + self.public_key, scalar, max_exponent=self.exponent + ) + return self._add_encoded(encoded) + + def _add_encoded(self, encoded): # 对EncodedNumber与标量encoded加法编码 + # 返回 E(a + b) + + if self.public_key != encoded.public_key: # 如果公钥与编码公钥不相同 + raise ValueError("不能使用不同的公钥,对数字进行编码!") + + a, b = self, encoded + # 对指数处理(使指数相同): + if a.exponent > b.exponent: + a = self.decrease_exponent_to(b.exponent) + elif a.exponent < b.exponent: + b = b.decrease_exponent_to(a.exponent) + + encrypted_scalar = a.public_key.raw_encrypt( + b.encoding, 1 + ) # 用公钥加密b.encoding后的标量 + + sum_ciphertext = a._raw_add(a.ciphertext(False), encrypted_scalar) # 进行相加操作 + return EncryptedNumber(a.public_key, sum_ciphertext, a.exponent) + + def _add_encrypted(self, other): # 对EncodedNumber与EncodedNumber加法加密 + if self.public_key != other.public_key: + raise ValueError("不能使用不同的公钥,对数字进行加密!") + + # 对指数处理(使指数相同): + a, b = self, other + if a.exponent > b.exponent: + a = self.decrease_exponent_to(b.exponent) + elif a.exponent < b.exponent: + b = b.decrease_exponent_to(a.exponent) + + sum_ciphertext = a._raw_add(a.ciphertext(False), b.ciphertext(False)) + return EncryptedNumber(a.public_key, sum_ciphertext, a.exponent) + + def _raw_add(self, e_a, e_b): # 对加密后的a,b直接进行相加,并返回未加密的结果 + return e_a * e_b % self.public_key.nsquare + + def _raw_mul(self, plaintext): # 对密文进行乘法运算,并返回未加密的结果 + # 检查乘数是否为int型: + if not isinstance(plaintext, int): + raise TypeError("期望密文应该是int型, 而不是 %s" % type(plaintext)) + + # 如果乘数是负数,或乘数比公钥的模(n)大: + if plaintext < 0 or plaintext >= self.public_key.n: + raise ValueError("超出可计算范围: %i" % plaintext) + + if self.public_key.n - self.public_key.max_int <= plaintext: + # 如果数据很大,则先反置一下再进行运算: + neg_c = invert(self.ciphertext(False), self.public_key.nsquare) + neg_scalar = self.public_key.n - plaintext + return powmod(neg_c, neg_scalar, self.public_key.nsquare) + else: + return powmod(self.ciphertext(False), plaintext, self.public_key.nsquare) + + def increment(self): # 定义自增运算 + return self + 1 + + def decrement(self): # 定义自减运算 + return self + 1 + + def cal_sum(self, *args): + result = 0 # 将初始值设置为0 + for i in args: + if not isinstance(i, (int, float, EncryptedNumber)): + raise TypeError("期望密文应该是int/float/EncryptedNumber型, 而不是 %s" % type(i)) + if isinstance(i, int or float): # 如果是 int 或 float 明文型,则先将明文加密在进行运算 + result += self.public_key.encrypt(i) + else: + result += i # 第一次循环:标量与密文相加;后面的循环,密文与密文相加 + return result + + def average(self, *args): # 定义求平均值 + total_sum = self.cal_sum( + *args + ) # 计算总和total是<__main__.EncryptedNumber object at 0x000002AB74FB9850> + + # # 如果总数超过了可计算范围 + # if total_sum > 91000: + # raise ValueError('超出可计算范围: %i' % total_sum) + + count = 0 # 定义count,用来统计参数的个数 + for _ in args: + count += 1 # count++ + return total_sum / count + + def weighted_average(self, *args): # 定义加权平均 def weighted_average(*args): + """PS: + args[0]: <__main__.EncryptedNumber object at 0x000001F7C1B6A610> + args[1]: 第一个参数 + args[2]: 给第一个参数设置的权值 + 。。。。。。 + """ + total_weight = sum(args[2::2]) # 计算权值的总和(使用切片操作从参数列表中取出索引为参数权值的元素) + if total_weight != 1: + raise TypeError("加权平均算法的权值设置错误!请重新设置!") + else: + # 计算加权和,其中: for i in range(0, len(args), 2) 表示以2为步长,从0递增,直到 i >= len(args)时: + result = sum(args[i] * args[i + 1] for i in range(1, len(args), 2)) + return result + + def reset(self): # 定义复位(置0运算) + zero = self.public_key.encrypt(0) # 用公钥对0进行加密 + return zero + + def calculate_variance(self, *args): # 定义求方差 + mean = self.average(*args) # 均值 + count = 0 # 定义count,用来统计参数的个数 + for _ in args: + count += 1 # count++ + variance = sum((x - mean) ** 2 for x in args) / (count - 1) + return variance + + # def IsZero(self): # 判断该数是否为0 + # ZERO = self + # zero = ZERO.public_key.encrypt(0) # 用公钥对0进行加密 + # flag = False # 用于判断该数是否为0(默认不为0) + # + # if self == zero: + # flag = True + # return flag + + # def POW(self, num): # 定义幂运算 + # if not isinstance(num, int): # 如果输入有问题 + # print("指数应输入 整数 标量!") + # else: + # result = self + # print(num) + # for i in [1, num]: + # result *= self + # return result + + +# def get_certificate(public_key): +# # 获得公钥的PEM编码的二进制形式 +# public_bytes = public_key.public_bytes( +# encoding=serialization.Encoding.PEM, format=serialization.PublicFormat.SubjectPublicKeyInfo) +# +# # 获得数字证书 +# cert = (public_bytes, hashlib.sha256(public_bytes).hexdigest()) # 元祖类型 +# return cert + + +if __name__ == "__main__": # 主函数 + Public_Key, Private_Key = generate_paillier_keypair() # 随机生成1024长的公钥和私钥 + x = 90000.23 + y = 90 + z = 0.5 + x_encrypted = Public_Key.encrypt(x) # 加密后的x + y_encrypted = Public_Key.encrypt(y) # 加密后的y + z_encrypted = Public_Key.encrypt(z) # 加密后的z + t_encrypted = x_encrypted + y_encrypted * 0.5 # 在x,y保密的情况下计算t,得到加密后的t(t_encrypted) + + # x_encrypted = x_encrypted.increment() # 自增 + # y_encrypted = y_encrypted.decrement() # 自减 + + # print(x_encrypted != y_encrypted) # 不相等 + # print(x_encrypted == y_encrypted) # 相等 + + # print(Private_Key.decrypt(~x_encrypted) ) # 取反 + + # total = x_encrypted.cal_sum(x_encrypted, y_encrypted, 0.5) # 求和函数 + # print("密文之和为:", Private_Key.decrypt(total)) + + # avg = x_encrypted.average(y_encrypted, z_encrypted, z_encrypted) # 求平均值函数 + # print("密文的平均值为:", Private_Key.decrypt(avg) ) # 只能对0~90090.73的数进行除法运算(除不尽) + + # weight_average = x_encrypted.weighted_average(x_encrypted, 0.1, y_encrypted, 0.3, z_encrypted, 0.6) # 加权平均函数 + # print("加权平均结果为:", Private_Key.decrypt(weight_average)) + + # variance = x_encrypted.calculate_variance(x_encrypted, y_encrypted) #求方差 + # print("方差为:", Private_Key.decrypt(variance)) + + # z_encrypted = z_encrypted.reset() # 复位函数 + # print("z复位后的结果为:", Private_Key.decrypt(z_encrypted) ) + + # print(x_encrypted ** x) # 相当于print(x_encrypted.POW(2) ) + # print(x_encrypted > y_encrypted) + + # print(type(Public_Key)) + # print(Public_Key) + + print(f"x + y * 0.5的结果是:{Private_Key.decrypt(t_encrypted)}") # 打印出t diff --git a/DataSearch.py b/DataSearch.py new file mode 100644 index 0000000..53953b7 --- /dev/null +++ b/DataSearch.py @@ -0,0 +1,295 @@ +import re +import pymysql # pylint: disable=e0401 # type: ignore + + +# 拆分sql对象 +def extract_tables(sql_statement): + # 使用正则表达式匹配FROM关键字之后的表名以及逗号分隔的表名: + pattern = r"FROM\s+([\w\s,]+)" + matches = re.findall(pattern, sql_statement, re.IGNORECASE) + # 提取逗号分隔的表名,并按逗号进行分割 + tabs = re.split(",", matches[0].strip()) + # 处理每个表名,去除空格和其他无关字符 + cleaned_tables = [] + for tab in tabs: + cleaned_tab = tab.strip() + if " " in cleaned_tab: + cleaned_tab = cleaned_tab.split()[0] + cleaned_tables.append(cleaned_tab) + return cleaned_tables # 返回列表 + + +# 拆分sql +def divide_sql(sql): + """ + 例如sql = "SELECT a FROM data1, data2, data3 WHERE a = b ORDER BY misc" + 拆分原语句 + """ + parts = re.split(r"(?i)from\s", sql) # 拆分"from "【无论大小写】 + head = parts[0] + "from " # SELECT a FROM + divide_sqls = [] + + if re.search(r"where", parts[1], flags=re.IGNORECASE): + data = re.split(r"(?i) where", parts[1]) # 拆分" where"【无论大小写】 + tail = " where" + data[1] # WHERE a = b ORDER BY misc + # message_p = "涉及到的数据源有:" + # print(message_p) + # time.sleep(sleep_time) + # + # message_p = data[0] + # print(message_p) # data1, data2, data3 + # time.sleep(sleep_time) + divide_providers = data[0].split(", ") + # total = len(divide_providers) + # message_p = "拆分结果如下:" + # print(message_p) + + for i in range(len(divide_providers)): + divide_sqls.append(head + divide_providers[i] + tail) + # message_p = str(i + 1) + ":" + divide_sqls[i] + # print(message_p) + else: + data = parts[1] # data1,data2,data3 + divide_providers = data.split(",") + for i in range(len(divide_providers)): + divide_sqls.append(head + divide_providers[i]) + # message_p = str(i + 1) + ":" + divide_sqls[i] + # print(message_p) + return divide_sqls + + +class SelectDatabase: # 定义类 + def __init__(self, database): + self.database = database # 赋值数据库名称 + + def ret_hospital(self): # 定义函数,返回字典——医院(HOS)这个表 + # 连接服务器Server的数据库: + db = pymysql.connect( + host="localhost", + user="root", + password="111111", + db=f"{self.database}", + charset="utf8", + ) + # 使用操作游标: + cursor = db.cursor() + sql = """SELECT * FROM HOS""" + cursor.execute(sql) + results = cursor.fetchall() # 获取查询结果的所有数据 + hospital_dict = {} # 创建一个空字典 + + # 遍历查询结果,将每条消息数据存储到字典中: + for row in results: + hos_id = row[0] # 医院编号(HOS_ID) + hos_name = row[1] # 医院名称(HOS_NAME) + hos_add = row[2] # 医院地址(HOS_ADD) + hos_tel = row[3] # 医院电话(HOS_TEL) + # hospital_dict["医院编号"] = hos_id + # hospital_dict["医院名称"] = hos_name + # hospital_dict["医院地址"] = hos_add + # hospital_dict["医院电话"] = hos_tel + # # 打印字典内容 + # print(hospital_dict) + """ 注释的输出形式: + {'医院编号': '001', '医院名称': '极光医院', '医院地址': '千里市广大区极光街道1-1', '医院电话': '023-6296666'} + {'医院编号': '002', '医院名称': '风舱医院', '医院地址': '风火市舱山区飞光街道1-1', '医院电话': '023-6286666'} + """ + + # 将每个属性的值存储在对应的列表中 + hospital_dict.setdefault("医院编号", []).append(hos_id) + hospital_dict.setdefault("医院名称", []).append(hos_name) + hospital_dict.setdefault("医院地址", []).append(hos_add) + hospital_dict.setdefault("医院电话", []).append(hos_tel) + + db.close() + """ 当前返回的字典形式: + {'医院编号': ['001', '002'], + '医院名称': ['极光医院', '风舱医院'], + '医院地址': ['千里市广大区极光街道1-1', + '风火市舱山区飞光街道1-1'], + '医院电话': ['023-6296666', '023-6286666']} + """ + # 返回字典 + return hospital_dict + + def ret_doctor(self): # 定义函数,返回字典——医生(DOC)这个表 + # 连接服务器Server的数据库: + db = pymysql.connect( + host="localhost", + user="root", + password="111111", + db=f"{self.database}", + charset="utf8", + ) + # 使用操作游标: + cursor = db.cursor() + sql = """SELECT * FROM DOC""" + cursor.execute(sql) + results = cursor.fetchall() # 获取查询结果的所有数据 + doctor_dict = {} # 创建一个空字典 + + # 遍历查询结果,将每条消息数据存储到字典中: + for row in results: + doc_id = row[0] # 医生编号(DOC_ID) + doc_name = row[1] # 医生名称(DOC_NAME) + doc_tel = row[2] # 医生电话(DOC_TEL) + doc_qua = row[3] # 医院资质(DOC_QUA) + # hospital_dict["医生编号"] = doc_id + # hospital_dict["医生名称"] = doc_name + # hospital_dict["医院地址"] = doc_tel + # hospital_dict["医院电话"] = doc_qua + # # 打印字典内容 + # print(doctor_dict) + + # 将每个属性的值存储在对应的列表中 + doctor_dict.setdefault("医院编号", []).append(doc_id) + doctor_dict.setdefault("医院名称", []).append(doc_name) + doctor_dict.setdefault("医院地址", []).append(doc_tel) + doctor_dict.setdefault("医院电话", []).append(doc_qua) + + db.close() + """ 当前返回的字典形式: + {'医院编号': ['001', '002', '003'], + '医院名称': ['神医华佗', '扁鹊', '还医生'], + '医院地址': ['19666666666', '13666666666', '13546981623'], + '医院电话': ['主任医师', '主任医师', '医师']} + """ + # 返回字典 + return doctor_dict + + def ret_patient(self): # 定义函数,返回字典——病人(PAT)这个表 + # 连接服务器Server的数据库: + db = pymysql.connect( + host="localhost", + user="root", + password="111111", + db=f"{self.database}", + charset="utf8", + ) + # 使用操作游标: + cursor = db.cursor() + sql = """SELECT * FROM PAT""" + cursor.execute(sql) + results = cursor.fetchall() # 获取查询结果的所有数据 + patient_dict = {} # 创建一个空字典 + + # 遍历查询结果,将每条消息数据存储到字典中: + for row in results: + pat_id = row[0] # 病人编号(PAT_ID) + pat_name = row[1] # 病人姓名(PAT_NAME) + pat_tel = row[2] # 病人电话(PAT_TEL) + # patient_dict["病人编号"] = pat_id + # patient_dict["病人姓名"] = pat_name + # patient_dict["病人电话"] = pat_tel + # # 打印字典内容 + # print(patient_dict) + + # 将每个属性的值存储在对应的列表中 + patient_dict.setdefault("病人编号", []).append(pat_id) + patient_dict.setdefault("病人姓名", []).append(pat_name) + patient_dict.setdefault("病人电话", []).append(pat_tel) + + db.close() + """ 当前返回的字典形式: + {'病人编号': ['001', '002', '003', '004'], + '病人姓名': ['曹操', '蔡桓公', '去还', '刘备'], + '病人电话': ['66666666666', '02666666666', '01234567891', '98765432101']} + """ + # 返回字典 + return patient_dict + + def ret_diagnosis(self): # 定义函数,返回字典——诊断(DIA)这个表 + # 连接服务器Server的数据库: + db = pymysql.connect( + host="localhost", + user="root", + password="111111", + db=f"{self.database}", + charset="utf8", + ) + # 使用操作游标: + cursor = db.cursor() + sql = """SELECT * FROM DIA""" + cursor.execute(sql) + results = cursor.fetchall() # 获取查询结果的所有数据 + diagnosis_dict = {} # 创建一个空字典 + + # 遍历查询结果,将每条消息数据存储到字典中: + for row in results: + dia_id = row[0] # 诊号(DIA_ID) + doc_id = row[1] # 医生编号(DOC_ID) + pat_id = row[2] # 病人编号(PAT_ID) + time = row[3] # 时间(TIME) + cases = row[4] # 病例(CASES) + symptom = row[5] # 症状(SYMPTOM) + # diagnosis_dict["诊号"] = dia_id + # diagnosis_dict["医生编号"] = doc_id + # diagnosis_dict["病人编号"] = pat_id + # diagnosis_dict["时间"] = time + # diagnosis_dict["病例"] = cases + # diagnosis_dict["症状"] = symptom + # # 打印字典内容 + # print(diagnosis_dict) + + # 将每个属性的值存储在对应的列表中 + diagnosis_dict.setdefault("诊号", []).append(dia_id) + diagnosis_dict.setdefault("医生编号", []).append(doc_id) + diagnosis_dict.setdefault("病人编号", []).append(pat_id) + diagnosis_dict.setdefault("时间", []).append(time) + diagnosis_dict.setdefault("病例", []).append(cases) + diagnosis_dict.setdefault("症状", []).append(symptom) + + db.close() + """ 当前返回的字典形式: + {'诊号': ['001', '002', '003', '004', '005'], + '医生编号': ['001', '001', '002', '003', '003'], + '病人编号': ['001', '001', '002', '003', '004'], + '时间': ['2015.03.04', '2015.05.16', '2016.12.30', '2017.01.15', '2017.01.15'], + '病例': ['小感冒', '慢性头痛', '通风', '中风', '脚部内伤'], '症状': ['突然头痛', '头非常痛,不能睡觉', '怕凉', '伤口大量出血,且发烧', '崴脚,走路痛']} + """ + # 返回字典 + return diagnosis_dict + + def ret_define(self, sql): # 定义函数,返回自定义信息的字典 + # 连接服务器Server的数据库: + db = pymysql.connect( + host="localhost", + user="root", + password="111111", + db=f"{self.database}", + charset="utf8", + ) + try: + # 使用操作游标: + cursor = db.cursor() + cursor.execute(sql) + results = cursor.fetchall() # 获取查询结果的所有数据 + # tables = extract_tables(sql) # 分离出sql语句中的表名 + + # 遍历查询结果,将每条消息都输出出来: + for row in results: + for i in range(len(row)): + print(f"{row[i]}\t", end="") + print() + + except: + print("查询权限不足!或查询语句出错!") + + +def main(hospital_name): + sql = """SELECT * + FROM HOS, DOC, PAT, DIA + WHERE DOC.DOC_ID = DIA.DOC_ID AND + PAT.PAT_ID = DIA.PAT_ID + """ + hospital_name = hospital_name.replace("\n", "") + if hospital_name == "xx大学附属医院": + database = SelectDatabase("Hospital1") + database.ret_define(sql) + + elif hospital_name == "xx阳光社区附属医院": + database = SelectDatabase("Hospital2") + database.ret_define(sql) + + else: + print("暂无记录!") diff --git a/README.md b/README.md new file mode 100644 index 0000000..b333c06 --- /dev/null +++ b/README.md @@ -0,0 +1,3 @@ +# algo_present + +算法分析课程的展示 diff --git a/background.png b/background.png new file mode 100644 index 0000000000000000000000000000000000000000..d14f18f0fc9e3206c9bde7e252e12bab192863c0 GIT binary patch literal 175359 zcmXtfbyVEk7wrtz!3QZ;D1$?BcNm=F?ykitZbNa`;_mR_#a)ZLyObiu-SzQ%Z@oW~ ztlZ?TtmK~TbN0UbhASyZp`#F?0002=uhQZw005lX`xXm=dvB3D7b$x`AUjHHI|BgN zF8|wr8y2J3Rd_dV`}a!>;I5Lv|&tBi>DNCdy5t?dfD<&1ZYZ&Aq;k+kM5s zEpzYILeW`ev|gu}M0fofsb2nU89@YlW3`OqV&f$~O&w7RP8>)JcsP1o3a%I%8X5|_ z^FHg3#0TlHB7ndUs9!wV?5h!mO!~HaH;M`!%E7Uvi@nX^=igO|v<2 z$SB~wmEZx2fMz=)BAy%a|9S&rMWD(5_jX9PDLBsD?|*lJ&Iu6A(tM4O_O`0T|8I*l z1TxA2@2ufAP&m8|--#A?nhPyQWeIujkuFmDaAhHMbxjQ+YR`bM^I-6C@nF#275IUq zfjBJiEGz&P06aAUc`KJ$e6qcYb*3ER(hUW2P*Ly@(B zShTz7c!NOmz=F8#6a<17P;yKmVPU)H(MI5hB*-pg9hf!+BAm4hgx1Qt;nIeZd?F4W zF)mtiOT|rq7l%dx&_IBF6eKf%kt6^}@J7{55U7&DRcZU5gOI)xupWy_G8HR~aG6HJ zs0p3|0M8ByfPe#nnF_`8{Fy|obH64c?W=%D(9y#yKin;`1)d5^vgCU`bZ^B3xgeQ% z>q>dsJpEy0ve=s%xvBM__+mk>fs(34XC}Ivt0EpGE{8(=i2;WJ)fR_XBIb7?1kZHU ztf;#dx?I*0pFce~6nX7%3wVJKOiW=~=aNV5GqQ3GgtNdJj^p*rJ<1JLl9>_%W1!Ln z7yB32*ynzR6AWra67>W75lI78OzE_wap9En!H~Gg92P|q^v7F*o+WZwGRg}`E4ON( zh^}4~pn}-+R@82uudFJ+5pT|Q3Oz18P6b3cM_cG0=Lc69LJI%_waD|u{rx~=RV~PJ z5K7_Z)z=9{(u1g8GbwvNWgtROM5wwS$V6Njab!r9dFwh&x{sCs3kwjR7N43%$;t{3 z&mLIoha5IV<|6kCm}+jU{GD3sDxE4S={qsxy{|d z=P64p*(z4vJTBbKt*X(OpR*zLX+x!w(GQN&FHY2&mU5qOKYcVSHx!z%UpA8t^a|rV zV564_SFzL=qZph2Xum9oi54Q=w^Rc=@eeGKnLM^bh(7GWfLX=@N+~_nkI%_uzBLd?EIX@R5+%L~l1Z%!HIC5$ea-+)<#&w14 z>)`35mHkN@a¬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%0AEOk@45b8 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�X zFVFeL@Jy0{)op_%fePw>&3G}B=!b#+{rjYGBP*7#@{)C$R2n2Rx%5uPymEOhv-<5x zx;R<;=L8Sbdxgp^b3#i~^MS`^?(^K4v!D9y6akF1fY+io-+t}UXDrnm2pD^PjKcb; zdc_7$=8Pk*r&>H&NSnm-3E&-cR^~lvT-~tIEBrX3RW^;ob0TLeoAyjlEig3`+r0X0gg8l$NNrPUe7%%8z#2}0gm z3Z9UoKqOXCbb};I>b4VkC&M_U3b{T4aPqHLS}ZO|>iC06k3BwV+GDz#$NEPe3UBc1 z3*P(Q4L2)MX0QXgmho0@%znd$ZMS3kuJ_z_n=!{6KIO@OL;*RuuV9{Z-DPQJfSd36 z3pTHn_wOA(H#Rjd-5jQpoP9_(Jb#Ob&H-PGzD{DHOcltAMOEkg$BClTJ~RE*AlOcR z@Yy9xmkU^S?W(k|d_bS#G(F$=y5d&#vf5pgQb?MTnv9bAs z52}_dtr>YlCTY;VVS&UaCAW@4T0K34OGDRiA7n z=ST0GOauqoYgndO(94fJ^UVE!Ri_)Gub-4TZ_VPW4Z`7=a?xg~!lNVPg^e*2(N=Y+Th8nHPzWU%c9J(}_R_g=N<`Qb)3h>0fj#r|f@d~j zDghw`5p1;|)swwfKUhKQJcu!HyIyHUk=YC0=e!p%9kr5j$B=g3 zX~#YI_zR}@*d4WpbnyN$aQn44-Ks;#nnIBvi22=xALI?L_ba8t&!QA z@CgcD9)84mq2$QM4Vf%L1;swzLHiEcXOG8!GkM@%8SG#5_QH*7mLtd1d$*m^U!VEyWJgX2Z!18aazx}o#Lh)g zln9Qw_uz~}@A7vmS`7P{kpqK~18I_gN3Dfihx}@Uu&_nZ@hCdx}wBnpon9f+5f%VV`DgRnH~|2;@7+6A6o$oN48G z2|*zkJORQHBfEuhK6w#z8^fYHRmg*vbpd?fzVW>>2{*uPnQQghv+N4Ib` zQzx525fJ%Q$f`0+`xl6x#S@tc4D#L}TL4C#I}@i?b3hd=@dkL1j68uLLLrbn#0<*b zESKu$Dme&9@zSi7tT1WrJgg9 !+2L@lhOenwsmqGunSvG85|trIRj`GioY^3q%H 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.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≫& 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 O0000" + cert_name} + return cert + + +# 公钥转公钥信息 +def key_to_data(key): + # str转bytes + byte = bytes.fromhex(key) + # bytes转PaillierPublicKey + data = pickle.loads(byte) + return data + + +# 加密字符串 +def str_to_encrypt(message, public_data): + # str 转 int + if message.isdigit(): + int_message = int(message) + else: + int_message = int.from_bytes(message.encode(), "big") + enc_message = public_data.encrypt(int_message) + print("int_message", int_message) + return enc_message + + +class MyClient: + def __init__(self): + # 实现GUI主界面框用到的参数 + self.root = None + self.data_text = None + self.name_text = None + self.message_text = None + self.window = None + # 初始化最近一次的待处理的sql申请信息 + self.recent_message = "" + # 初始化本端名 + self.name = "" + # 初始化sql + self.sql = "" + + # 准备界面函数 + self.root_window() + self.give_name() + + # 生成私钥和公钥信息 + ( + self.private_key_data, + self.public_key_data, + ) = generate_key_pair_data() # PaillierPublicKey 类型 + # 生成私钥和公钥字符串 + self.private_key, self.public_key = self.generate_key_pair() + # 初始化数字证书 + self.certificate = {} # 等名字输入了再生成 + # 初始化socket + self.client = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + + # 初始化根界面 + def root_window(self): + self.root = tk.Tk() + self.root.title("数据提供方智慧医疗SQL查询系统") + self.root.geometry("450x540") + # 增加背景图片 + global photo + photo = ImageTk.PhotoImage(file="background.png") + label = Label(self.root, image=photo) + label.pack() + self.create_widgets() # 调用函数创建功能按钮 + + # 为本端取名 + def give_name(self): + self.window = tk.Toplevel() + self.window.title("命名窗口") + self.window.geometry("300x320") + # 初始化信息输入框 + self.name_text = tk.StringVar() + tk.Label(self.window, bg="green", text="请为本端取名:").place(x=50, y=100) + input_message = tk.Entry(self.window, textvariable=self.name_text) + input_message.pack() + input_message.place(x=80, y=150) + # 确认按钮及位置 + bt_confirm = tk.Button( + self.window, bg="green", text="确认", width=10, command=self.get_name + ) + bt_confirm.pack() + bt_confirm.place(x=125, y=220) + + # 读取输入框的name,保存在self.name中 + def get_name(self): + self.name = self.name_text.get() + messagebox.showinfo("提示", "命名成功!") + self.window.destroy() + self.root.title("<数据提供方>" + self.name + "的智慧医疗系统") + + # 安全通信 + def safe_connect(self): + if self.certificate != {}: + # 与服务器端地址建立通信, 8080为服务端程序的端口号 + self.client.connect(("localhost", 1111)) + # 为接收消息函数添加线程 + threading.Thread(target=self.get_message).start() + time.sleep(3) + else: + messagebox.showinfo("提示", "请先生成你的公钥证书!") + + # 向平台发送本地公钥信息 + def send_public_key(self): + print("正在向平台发送本地公钥信息...") + self.client.send(str(self.certificate).encode()) + print(self.certificate) + time.sleep(sleep_time) + print("发送成功!") + + # 产生公私钥字符串 + def generate_key_pair(self): + # Paillier 转 bytes + public_key_bytes = pickle.dumps(self.public_key_data) + private_key_bytes = pickle.dumps(self.private_key_data) + # bytes 转 str + public_key = public_key_bytes.hex() + private_key = private_key_bytes.hex() + return private_key, public_key + + # 打印公钥 + def print_public_key(self): + print("本地公钥信息为:") + print(self.public_key_data) + print("打印公钥如下:", end="") + public_key_str = "\n".join( + self.public_key[i : i + 60] for i in range(0, len(self.public_key), 60) + ) + pack_public_key = ( + "\n-----BEGIN PUBLIC KEY-----\n" + + public_key_str + + "\n-----END PUBLIC KEY-----\n" + ) + print(pack_public_key) + messagebox.showinfo("本地公钥", pack_public_key) # GUI界面显示 + + # 打印私钥 + def print_private_key(self): + print("本地私钥信息为:") + print(self.private_key_data) + private_key_str = "\n".join( + self.private_key[i : i + 60] for i in range(0, len(self.public_key), 60) + ) + pack_private_key = ( + "-----BEGIN PRIVATE KEY-----\n" + + private_key_str + + "\n-----END PRIVATE KEY-----\n" + ) + print("打印私钥如下:", end="") + print(pack_private_key) + messagebox.showinfo("本地私钥", pack_private_key) # GUI界面显示 + + # 打印证书 + def print_certificate(self): + if self.name != "": + self.certificate = get_certificate(self.public_key, self.name) + print("本地公钥证书为:") + message = "" + for key, value in self.certificate.items(): + print(key, ":\n", value) + message += key + ":\n" + value + "\n" + messagebox.showinfo("本地公钥证书", message) + else: + messagebox.showinfo("提示", "请先为平台命名!") + + # 接收消息 + def get_message(self): + while True: + # 接收连接结果信息 + message = self.client.recv(4096).decode() + print(message) + if message == "安全多方计算平台:单向授权成功!": + self.client.send("请发送平台的完整公钥证书".encode()) + while True: + message = self.client.recv(4096).decode() # 公钥字符串比较长,要多一点 + if message.startswith("{'public_key':"): # 等待接收平台公钥证书 + cert = eval(message) + messagebox.showinfo("连接结果", "与平台连接成功!") + print("接收到的平台的公钥证书如下:") + message = "" + for key, value in cert.items(): + if isinstance(value, bytes): + value = value.decode() + print(key, ":\n", value) + message += key + ":\n" + value + messagebox.showinfo("平台的公钥证书", message) + server_public_key = ( + cert["public_key"] + .split("-----END PUBLIC KEY-----")[0] + .split("-----BEGIN PUBLIC KEY-----")[1] + ) + server_public_key = server_public_key.replace("\n", "") + print("提取到的server_public_key:") + print(server_public_key) + global server_public_key_data + server_public_key_data = key_to_data(server_public_key) + print("对应的server_public_key_data:") + print(server_public_key_data) + messagebox.showinfo("平台的公钥信息", server_public_key_data) + self.client.send("认证成功!".encode()) + self.send_public_key() # 单向认证后把自己的公钥证书发给平台实现双向认证 + break + else: + print("错误的Message", message) + elif message == "安全多方计算平台:双向授权成功!": + messagebox.showinfo("平台发来的消息", "与平台的双向认证成功!") + self.client.send("请求查询方信息!".encode()) + cert = self.client.recv(4096).decode() + cert = eval(cert) + message_p = "接收到一则非平台的公钥证书" + print(message_p) + # messagebox.showinfo("提示", message_p) + message = "" + for key, value in cert.items(): + if isinstance(value, bytes): + value = value.decode() + print(key, ":\n", value) + message += key + ":\n" + value + "\n" + # messagebox.showinfo("数据查询方的公钥证书", message) + search_public_key = ( + cert["public_key"] + .split("-----END PUBLIC KEY-----")[0] + .split("-----BEGIN PUBLIC KEY-----")[1] + ) + search_public_key = search_public_key.replace("\n", "") + print("提取到的search_public_key:") + print(search_public_key) + global search_public_key_data + search_public_key_data = key_to_data(search_public_key) + print("对应的search_public_key_data:") + print(search_public_key_data) + # messagebox.showinfo("数据查询方的公钥信息", search_public_key_data) + + elif message.startswith("01001010"): + message = message.split("01001010", 1)[ + 1 + ] # [0]是空字符串,已测试。只切一次是防止密文出现和头部一样的信息被误切。 + # 使用私钥解密消息,获得sql明文 + print("接收到的sql密文:", message) + int_message = int(message) + # Ciphertext转EncryptedNumber + Encrpt_message = CoreAlgorithm.EncryptedNumber( + self.public_key_data, int_message, exponent=0 + ) + dec_int_message = self.private_key_data.decrypt(Encrpt_message) + dec_message = dec_int_message.to_bytes( + (dec_int_message.bit_length() + 7) // 8, "big" + ).decode() + print("解密后的消息为:", dec_message) + self.sql = dec_message.split("||")[2] + print("收到已授权方的sql:", self.sql) + self.design_window() + + # --------------------------接收sql消息的窗口设计函数------------------------- + + # 接受调用数据的请求 + def send_accept_reply(self): + self.client.send( + ( + "01001001" + + str(self.client.getsockname()) + + "||" + + self.name + + "同意了你的数据申请。" + ).encode() + ) + self.window.destroy() + self.window = tk.Toplevel() + self.window.title("命名窗口") + self.window.geometry("300x320") + # 信息输入框 + self.data_text = tk.StringVar() + tk.Label(self.window, bg="green", text="请输入要调用的信息:").place(x=50, y=100) + input_message = tk.Entry(self.window, textvariable=self.data_text) + input_message.pack() + input_message.place(x=80, y=150) + # 确认按钮及位置 + bt_confirm = tk.Button( + self.window, bg="green", text="确认", width=10, command=self.send_data + ) + bt_confirm.pack() + bt_confirm.place(x=125, y=220) + + # 发送要调用的信息 + def send_data(self): + message = self.data_text.get() + print(message, type(message)) + # 加密并封装消息 + print("正在封装并加密消息...") + global search_public_key_data + enc_message = str_to_encrypt(message, search_public_key_data).ciphertext() + pack_message = "01001100" + str(enc_message) + print("正在发送消息给平台...") + self.client.send(pack_message.encode()) + print("发送成功!") + messagebox.showinfo("提示", "发送成功!") + self.window.destroy() + + # 拒绝调用数据的请求 + def send_decline_reply(self): + self.client.send( + ( + "01001001" + str(self.client.getsockname()) + "||" + "想得美哦!不给(*^▽^*)" + ).encode() + ) + messagebox.showinfo("提示", "已拒绝!") + self.window.destroy() + + # 稍后处理调用数据的请求 + def wait_reply(self): + self.window.destroy() + + # 接收到sql信息的专属窗口 + def design_window(self): + if self.sql != "": + self.window = tk.Toplevel() + self.window.title("平台发来的消息") + self.window.geometry("550x320") # 设定窗口大小 + self.recent_message = "收到已授权方的sql:" + self.sql + tk.Label(self.window, bg="green", text=self.recent_message).place( + x=50, y=100 + ) + # 创建接受按钮 + accept_button = tk.Button( + self.window, text="接受", width=15, command=self.send_accept_reply + ) + accept_button.place(x=90, y=220) + + # 创建拒绝按钮 + decline_button = tk.Button( + self.window, text="拒绝", width=15, command=self.send_decline_reply + ) + decline_button.place(x=225, y=220) + + # 创建稍后处理按钮 + decline_button = tk.Button( + self.window, text="稍后处理", width=15, command=self.wait_reply + ) + decline_button.place(x=360, y=220) + else: + messagebox.showinfo("提示", "暂无待处理的信息。") + + # ----------------------------------------------------------------------- + + # 调用本地信息库 + def search_data(self): + output = StringIO() # 用于保存打印信息 + sys.stdout = output # 重定向sys.stdout到StringIO + print(self.name) + DataSearch.main(self.name) + sys.stdout = sys.__stdout__ + long_text = output.getvalue() + print(long_text) + # 创建显示窗口 + window = tk.Toplevel() + # 创建滚动文本部件 + roll = scrolledtext.ScrolledText(window, width=200, height=40) + + # 打印超长字符串 + roll.insert(tk.END, long_text) + roll.pack() + + # --------------------------主界面的按键功能绑定-------------------------------- + # 界面按键 + def create_widgets(self): + # 创建按钮和标签等部件并使用ttk和Style进行美化 + style = Style(theme="darkly") # 指定样式主题 + self.root = style.master + button1 = ttk.Button( + self.root, + text="查看我的公钥", + bootstyle="info-outline", + command=self.print_public_key, + width=30, + ) + button1.place(x=104, y=310) + button2 = ttk.Button( + self.root, + text="查看我的私钥", + bootstyle="info-outline", + command=self.print_private_key, + width=30, + ) + button2.place(x=104, y=348) + button3 = ttk.Button( + self.root, + text="生成我的证书", + bootstyle="info-outline", + command=self.print_certificate, + width=30, + ) + button3.place(x=104, y=383) + button4 = ttk.Button( + self.root, + text="获取平台认证", + bootstyle="info-outline", + command=self.safe_connect, + width=30, + ) + button4.place(x=104, y=418) + button5 = ttk.Button( + self.root, + text="处理请求信息", + bootstyle="info-outline", + command=self.design_window, + width=30, + ) + button5.place(x=104, y=453) + button6 = ttk.Button( + self.root, + text="查看医疗记录", + bootstyle="info-outline", + command=self.search_data, + width=30, + ) + button6.place(x=104, y=488) + + # GUI界面 + def run(self): + self.root.mainloop() + + +# ----------------------------------------------------主程序---------------------------------------------------- +global server_public_key_data, search_public_key_data, photo # 使用全局变量,否则图片可能会被回收,不显示 + +# GUI界面 +app = MyClient() +app.run() diff --git a/data_requirer.py b/data_requirer.py new file mode 100644 index 0000000..80edbb0 --- /dev/null +++ b/data_requirer.py @@ -0,0 +1,508 @@ +import socket +import sys +import threading +import time +import pickle +from io import StringIO + +import CoreAlgorithm +import DataSearch + +# 审批回执类消息head:01001001 +# sql密文类消息head:01001010 +# 元数据类消息head:01001100 + +# 实现GUI界面 +import tkinter as tk +from tkinter import * +from tkinter import messagebox, scrolledtext +from PIL import ImageTk + +# 美化 +from ttkbootstrap import Style # pylint: disable=e0401 # type: ignore +from tkinter import ttk + + +# 用于生成本地公钥和私钥 +def generate_key_pair_data(): + public_key_data, private_key_data = CoreAlgorithm.generate_paillier_keypair() + return private_key_data, public_key_data + + +# 用公钥为本地生成数字证书 +def get_certificate(temp_public_key, cert_name): + public_key_str = "\n".join( + temp_public_key[i : i + 60] for i in range(0, len(temp_public_key), 60) + ) + pack_public_key = ( + "-----BEGIN PUBLIC KEY-----\n" + public_key_str + "\n-----END PUBLIC KEY-----" + ) + cert = {"public_key": pack_public_key, "name": "<数据查询方>" + cert_name} + return cert + + +# 公钥转公钥信息 +def key_to_data(key): + # str转bytes + byte = bytes.fromhex(key) + # bytes转PaillierPublicKey + data = pickle.loads(byte) + return data + + +# 加密字符串 +def str_to_encrypt(message, public_data): + # str 转 int + if message.isdigit(): + int_message = int(message) + else: + int_message = int.from_bytes(message.encode(), "big") + enc_message = public_data.encrypt(int_message) + print("int_message", int_message) + return enc_message + + +class MyClient: + def __init__(self): + # 实现GUI主界面框用到的参数 + self.root = None + self.data_text = None + self.name_text = None + self.recent_message = None + self.window = None + self.sql_text = None + # 初始化本端名 + self.name = "" + # 初始化sql + self.sql = "" + + # 准备界面函数 + self.root_window() + self.give_name() + + # 生成私钥和公钥信息 + ( + self.private_key_data, + self.public_key_data, + ) = generate_key_pair_data() # PaillierPublicKey 类型 + # 生成私钥和公钥字符串 + self.private_key, self.public_key = self.generate_key_pair() + # 获取数字证书 + self.certificate = {} # 等名字输入了再生成 + # 初始化socket + self.client = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + + # 初始化根界面 + def root_window(self): + self.root = tk.Tk() + self.root.title("数据查询方智慧医疗SQL查询系统") + self.root.geometry("450x660") + # 增加背景图片 + global photo + photo = ImageTk.PhotoImage(file="background.png") + label = Label(self.root, image=photo) + label.pack() + self.create_widgets() # 调用函数创建功能按钮 + + # 为本端取名 + def give_name(self): + self.window = tk.Toplevel() + self.window.title("命名窗口") + self.window.geometry("300x320") + # 初始化信息输入框 + self.name_text = tk.StringVar() + tk.Label(self.window, bg="green", text="请为本端取名:").place(x=50, y=100) + input_message = tk.Entry(self.window, textvariable=self.name_text) + input_message.pack() + input_message.place(x=80, y=150) + # 确认按钮及位置 + bt_confirm = tk.Button( + self.window, bg="green", text="确认", width=10, command=self.get_name + ) + bt_confirm.pack() + bt_confirm.place(x=125, y=220) + + # 读取输入框的name,保存在self.name中 + def get_name(self): + self.name = self.name_text.get() + messagebox.showinfo("提示", "命名成功!") + self.window.destroy() + self.root.title("<数据查询方>" + self.name + "的智慧医疗系统") + + # 编写sql + def create_sql(self): + self.window = tk.Toplevel() # 建立窗口 + self.window.title("编写sql语句") # 为窗口命名 + self.window.geometry("580x270") # 设定窗口大小 + # 初始化信息输入框 + self.sql_text = tk.StringVar() + tk.Label(self.window, bg="green", text="请输入您需要的sql查询:").place(x=70, y=50) + input_message = tk.Entry(self.window, textvariable=self.sql_text, width=60) + input_message.place(x=70, y=100) + # 初始化确认按钮 + bt_confirm = tk.Button( + self.window, bg="green", text="确认", width=60, command=self.get_sql + ) + bt_confirm.place(x=70, y=150) + # self.sql = input("请编写你的sql:") + + # 读取输入框的sql语句,保存在self.sql中 + def get_sql(self): + self.sql = self.sql_text.get() + if self.sql != "": + messagebox.showinfo("提示", "编写成功!") + else: + messagebox.showinfo("提示", "编写失败!语句不能为空!") + self.window.destroy() + + # 安全通信 + def safe_connect(self): + if self.certificate != {}: + # 与服务器端地址建立通信, 8080为服务端程序的端口号 + self.client.connect(("localhost", 1111)) + # 为接收消息函数添加线程 + threading.Thread(target=self.get_message).start() + time.sleep(3) + else: + messagebox.showinfo("提示", "请先生成你的公钥证书!") + + # 向平台发送本地公钥信息 + def send_public_key(self): + print("正在向平台发送本地公钥信息...") + self.client.send(str(self.certificate).encode()) + time.sleep(1) + print("发送成功!") + + # 发送sql + def send_sql(self): + if self.sql != "": + print("正在封装并加密消息...") + message = self.deal_sql(self.sql) + print("处理后的message:", message) + print("正在发送消息给平台...") + self.client.send(message.encode()) + print("发送成功!") + messagebox.showinfo("提示", "发送成功!") # GUI界面显示 + else: + messagebox.showinfo("提示", "请先进行编写sql的操作确认您的需求!") # GUI界面显示 + + # 加密封装sql语句 + def deal_sql(self, context): + message = str(self.client.getsockname()) + "||" + context + global server_public_key_data + enc_message = str_to_encrypt(message, server_public_key_data) # 用平台的公钥进行加密 + return "01001010" + str(enc_message.ciphertext()) # int 型 + + # 产生公私钥字符串 + def generate_key_pair(self): + # Paillier 转 bytes + public_key_bytes = pickle.dumps(self.public_key_data) + private_key_bytes = pickle.dumps(self.private_key_data) + # bytes 转 str + public_key = public_key_bytes.hex() + private_key = private_key_bytes.hex() + return private_key, public_key + + # 打印公钥 + def print_public_key(self): + print("本地公钥信息为:") + print(self.public_key_data) + print("打印公钥如下:") + public_key_str = "\n".join( + self.public_key[i : i + 60] for i in range(0, len(self.public_key), 60) + ) + pack_public_key = ( + "\n-----BEGIN PUBLIC KEY-----\n" + + public_key_str + + "\n-----END PUBLIC KEY-----\n" + ) + print(pack_public_key) + # show_list() + messagebox.showinfo("本地公钥", pack_public_key) # GUI界面显示 + + # 打印私钥 + def print_private_key(self): + print("本地私钥信息为:") + print(self.private_key_data) + private_key_str = "\n".join( + self.private_key[i : i + 60] for i in range(0, len(self.public_key), 60) + ) + pack_private_key = ( + "-----BEGIN PRIVATE KEY-----\n" + + private_key_str + + "\n-----END PRIVATE KEY-----\n" + ) + print("打印私钥如下:") + print(pack_private_key) + messagebox.showinfo("本地私钥", pack_private_key) # GUI界面显示 + + # 打印证书 + def print_certificate(self): + if self.name != "": + self.certificate = get_certificate(self.public_key, self.name) + print("本地公钥证书为:") + message = "" + for key, value in self.certificate.items(): + print(key, ":\n", value) + message += key + ":\n" + value + "\n" + messagebox.showinfo("本地公钥证书", message) + else: + messagebox.showinfo("提示", "请先为平台命名!") + + # 接收消息 + def get_message(self): + while True: + # 接收连接结果信息 + message = self.client.recv(4096).decode() + print(message) + if message == "安全多方计算平台:单向授权成功!": + self.client.send("请发送平台的完整公钥证书".encode()) + while True: + message = self.client.recv(4096).decode() + if message.startswith("{'public_key':"): + cert = eval(message) + messagebox.showinfo("连接结果", "与平台连接成功!") + print("接收到的平台的公钥证书如下:") + message = "" + for key, value in cert.items(): + if isinstance(value, bytes): + value = value.decode() + print(key, ":\n", value) + message += key + ":\n" + value + messagebox.showinfo("平台的公钥证书", message) + server_public_key = ( + cert["public_key"] + .split("-----END PUBLIC KEY-----")[0] + .split("-----BEGIN PUBLIC KEY-----")[1] + ) + server_public_key = server_public_key.replace("\n", "") + print("提取到的server_public_key:") + print(server_public_key) + global server_public_key_data + server_public_key_data = key_to_data(server_public_key) + print("对应的server_public_key_data:") + print(server_public_key_data) + messagebox.showinfo("平台的公钥信息", server_public_key_data) + self.client.send("认证成功!".encode()) + self.send_public_key() # 单向认证后把自己的公钥证书发给平台实现双向认证 + break + else: + print("错误的Message", message) + elif message == "安全多方计算平台:双向授权成功!": + messagebox.showinfo("平台发来的消息", "与平台的双向认证成功!") + + elif message.startswith("01001010"): + message = message.split("01001010", 1)[ + 1 + ] # [0]是空字符串,已测试。只切一次是防止密文出现和头部一样的信息被误切。 + # 使用私钥解密消息,获得sql明文 + print("接收到的sql密文:", message) + int_message = int(message) + # Ciphertext转EncryptedNumber + Encrpt_message = CoreAlgorithm.EncryptedNumber( + self.public_key_data, int_message, exponent=0 + ) + dec_int_message = self.private_key_data.decrypt(Encrpt_message) + dec_message = dec_int_message.to_bytes( + (dec_int_message.bit_length() + 7) // 8, "big" + ).decode() + print("解密后的消息为:", dec_message) + self.sql = dec_message.split("||")[2] + print("收到已授权方的sql:", self.sql) + self.design_window() + + elif message.startswith("结果:"): + message = message.split("结果:")[1] + if message != "正在等待接收其他信息...": + int_message = int(message) + # Ciphertext转EncryptedNumber + Encrpt_message = CoreAlgorithm.EncryptedNumber( + self.public_key_data, int_message, exponent=0 + ) + dec_int_message = self.private_key_data.decrypt(Encrpt_message) + print("分析结果:", dec_int_message) + messagebox.showinfo("分析结果", dec_int_message) + else: + messagebox.showinfo("提示", message) + + # --------------------------接收sql消息的窗口设计函数------------------------- + # 接受调用数据的请求 + def send_accept_reply(self): + self.client.send( + ( + "01001001" + str(self.client.getsockname()) + "||安全多方平台同意了你的数据申请。" + ).encode() + ) + self.window = tk.Toplevel() + self.window.title("命名窗口") + self.window.geometry("300x320") + # 信息输入框 + self.data_text = tk.StringVar() + tk.Label(self.window, bg="green", text="请输入要调用的信息:").place(x=50, y=100) + input_message = tk.Entry(self.window, textvariable=self.data_text) + input_message.pack() + input_message.place(x=80, y=150) + # 确认按钮及位置 + bt_confirm = tk.Button( + self.window, bg="green", text="确认", width=10, command=self.send_data + ) + bt_confirm.pack() + bt_confirm.place(x=125, y=220) + + # 发送要调用的信息 + def send_data(self): + message = self.name_text.get() + # 加密并封装消息 + print("正在封装并加密消息...") + enc_message = str_to_encrypt(message, self.public_key_data).ciphertext() + pack_message = "01001100" + str(enc_message) + print("正在发送消息给平台...") + self.client.send(pack_message.encode()) + print("发送成功!") + messagebox.showinfo("提示", "发送成功!") + self.window.destroy() + + # 拒绝调用数据的请求 + def send_decline_reply(self): + self.client.send( + ( + "01001001" + str(self.client.getsockname()) + "||" + "想得美哦!不给(*^▽^*)" + ).encode() + ) + messagebox.showinfo("提示", "已拒绝!") + self.window.destroy() + + # 稍后处理调用数据的请求 + def wait_reply(self): + self.window.destroy() + + # 接收到sql信息的专属窗口 + def design_window(self): + if self.sql != "": + self.window = tk.Toplevel() + self.window.title("平台发来的消息") + self.window.geometry("550x320") # 设定窗口大小 + self.recent_message = "收到已授权方的sql:" + self.sql + tk.Label(self.window, bg="green", text=self.recent_message).place( + x=50, y=100 + ) + # 创建接受按钮 + accept_button = tk.Button( + self.window, text="接受", width=15, command=self.send_accept_reply + ) + accept_button.place(x=90, y=220) + + # 创建拒绝按钮 + decline_button = tk.Button( + self.window, text="拒绝", width=15, command=self.send_decline_reply + ) + decline_button.place(x=225, y=220) + + # 创建稍后处理按钮 + decline_button = tk.Button( + self.window, text="稍后处理", width=15, command=self.wait_reply + ) + decline_button.place(x=360, y=220) + else: + messagebox.showinfo("提示", "暂无待处理的信息。") + + # -------------------------------------------------------------------------- + # 调用本地信息库 + def search_data(self): + output = StringIO() # 用于保存打印信息 + sys.stdout = output # 重定向sys.stdout到StringIO + print(self.name) + DataSearch.main(self.name) + sys.stdout = sys.__stdout__ + long_text = output.getvalue() + print(long_text) + # 创建显示窗口 + window = tk.Toplevel() + # 创建滚动文本部件 + roll = scrolledtext.ScrolledText(window, width=200, height=40) + # 打印超长字符串 + roll.insert(tk.END, long_text) + roll.pack() + + # --------------------------主界面的按键功能绑定-------------------------------- + # 界面按键 + def create_widgets(self): + # 创建按钮和标签等部件并使用ttk和Style进行美化 + style = Style(theme="darkly") # 指定样式主题 + self.root = style.master + button1 = ttk.Button( + self.root, + text="查看我的公钥", + bootstyle="info-outline", + command=self.print_public_key, + width=30, + ) + button1.place(x=104, y=360) + button2 = ttk.Button( + self.root, + text="查看我的私钥", + bootstyle="info-outline", + command=self.print_private_key, + width=30, + ) + button2.place(x=104, y=398) + button3 = ttk.Button( + self.root, + text="生成我的证书", + bootstyle="info-outline", + command=self.print_certificate, + width=30, + ) + button3.place(x=104, y=433) + button4 = ttk.Button( + self.root, + text="获取平台认证", + bootstyle="info-outline", + command=self.safe_connect, + width=30, + ) + button4.place(x=104, y=468) + button5 = ttk.Button( + self.root, + text="调用SQL查询", + bootstyle="info-outline", + command=self.create_sql, + width=30, + ) + button5.place(x=104, y=503) + button6 = ttk.Button( + self.root, + text="发送SQL请求", + bootstyle="info-outline", + command=self.send_sql, + width=30, + ) + button6.place(x=104, y=538) + button7 = ttk.Button( + self.root, + text="处理请求信息", + bootstyle="info-outline", + command=self.design_window, + width=30, + ) + button7.place(x=104, y=573) + button8 = ttk.Button( + self.root, + text="查看医疗记录", + bootstyle="info-outline", + command=self.search_data, + width=30, + ) + button8.place(x=104, y=608) + + # GUI界面 + def run(self): + self.root.mainloop() + + +# ----------------------------------------------------主程序---------------------------------------------------- +global server_public_key_data, photo # 使用全局变量,否则图片可能会被回收,不显示 + +# GUI界面 +app = MyClient() +app.run() diff --git a/encoding.py b/encoding.py new file mode 100644 index 0000000..97469a1 --- /dev/null +++ b/encoding.py @@ -0,0 +1,162 @@ +import math +import sys + + +class EncodedNumber(object): + BASE = 16 + """Base to use when exponentiating. Larger `BASE` means + that :attr:`exponent` leaks less information. If you vary this, + you'll have to manually inform anyone decoding your numbers. + """ + LOG2_BASE = math.log(BASE, 2) + FLOAT_MANTISSA_BITS = sys.float_info.mant_dig + + def __init__(self, public_key, encoding, exponent): + self.public_key = public_key + self.encoding = encoding + self.exponent = exponent + + @classmethod + def encode(cls, public_key, scalar, precision=None, max_exponent=None): + """Return an encoding of an int or float. + + This encoding is carefully chosen so that it supports the same + operations as the Paillier cryptosystem. + + If *scalar* is a float, first approximate it as an int, `int_rep`: + + scalar = int_rep * (:attr:`BASE` ** :attr:`exponent`), + + for some (typically negative) integer exponent, which can be + tuned using *precision* and *max_exponent*. Specifically, + :attr:`exponent` is chosen to be equal to or less than + *max_exponent*, and such that the number *precision* is not + rounded to zero. + + Having found an integer representation for the float (or having + been given an int `scalar`), we then represent this integer as + a non-negative integer < :attr:`~PaillierPublicKey.temp_n`. + + Paillier homomorphic arithemetic works modulo + :attr:`~PaillierPublicKey.temp_n`. We take the convention that a + number x < temp_n/3 is positive, and that a number x > 2n/3 is + negative. The range temp_n/3 < x < 2n/3 allows for overflow + detection. + + Args: + public_key (PaillierPublicKey): public key for which to encode + (this is necessary because :attr:`~PaillierPublicKey.temp_n` + varies). + scalar: an int or float to be encrypted. + If int, it must satisfy abs(*value*) < + :attr:`~PaillierPublicKey.temp_n`/3. + If float, it must satisfy abs(*value* / *precision*) << + :attr:`~PaillierPublicKey.temp_n`/3 + (i.e. if a float is near the limit then detectable + overflow may still occur) + precision (float): Choose exponent (i.e. fix the precision) so + that this number is distinguishable from zero. If `scalar` + is a float, then this is set so that minimal precision is + lost. Lower precision leads to smaller encodings, which + might yield faster computation. + max_exponent (int): Ensure that the exponent of the returned + `EncryptedNumber` is at most this. + + Returns: + EncodedNumber: Encoded form of *scalar*, ready for encryption + against *publickey*. + """ + # Calculate the maximum exponent for desired precision + if precision is None: + if isinstance(scalar, int): + prec_exponent = 0 + elif isinstance(scalar, float): + # Encode with *at least* as much precision as the python float + # What's the base-2 exponent on the float? + bin_flt_exponent = math.frexp(scalar)[1] + + # What's the base-2 exponent of the least significant bit? + # The least significant bit has value 2 ** bin_lsb_exponent + bin_lsb_exponent = bin_flt_exponent - cls.FLOAT_MANTISSA_BITS + + # What's the corresponding base BASE exponent? Round that down. + prec_exponent = math.floor(bin_lsb_exponent / cls.LOG2_BASE) + else: + raise TypeError("Don't know the precision of type %s." + % type(scalar)) + else: + prec_exponent = math.floor(math.log(precision, cls.BASE)) + + # Remember exponents are negative for numbers < 1. + # If we're going to store numbers with a more negative + # exponent than demanded by the precision, then we may + # as well bump up the actual precision. + if max_exponent is None: + exponent = prec_exponent + else: + exponent = min(max_exponent, prec_exponent) + + int_rep = int(round(scalar * pow(cls.BASE, -exponent))) + + if abs(int_rep) > public_key.max_int: + raise ValueError('Integer needs to be within +/- %d but got %d' + % (public_key.max_int, int_rep)) + + # Wrap negative numbers by adding temp_n + return cls(public_key, int_rep % public_key.n, exponent) + + def decode(self): + """Decode plaintext and return the result. + + Returns: + an int or float: the decoded number. N.B. if the number + returned is an integer, it will not be of type float. + + Raises: + OverflowError: if overflow is detected in the decrypted number. + """ + if self.encoding >= self.public_key.n: + # Should be mod temp_n + raise ValueError('Attempted to decode corrupted number') + elif self.encoding <= self.public_key.max_int: + # Positive + mantissa = self.encoding + elif self.encoding >= self.public_key.n - self.public_key.max_int: + # Negative + mantissa = self.encoding - self.public_key.n + else: + raise OverflowError('Overflow detected in decrypted number') + + return mantissa * pow(self.BASE, self.exponent) + + def decrease_exponent_to(self, new_exp): + """Return an `EncodedNumber` with same value but lower exponent. + + If we multiply the encoded value by :attr:`BASE` and decrement + :attr:`exponent`, then the decoded value does not change. Thus + we can almost arbitrarily ratchet down the exponent of an + :class:`EncodedNumber` - we only run into trouble when the encoded + integer overflows. There may not be a warning if this happens. + + This is necessary when adding :class:`EncodedNumber` instances, + and can also be useful to hide information about the precision + of numbers - e.g. a protocol can fix the exponent of all + transmitted :class:`EncodedNumber` to some lower bound(s). + + Args: + new_exp (int): the desired exponent. + + Returns: + EncodedNumber: Instance with the same value and desired + exponent. + + Raises: + ValueError: You tried to increase the exponent, which can't be + done without decryption. + """ + if new_exp > self.exponent: + raise ValueError('New exponent %i should be more negative than' + 'old exponent %i' % (new_exp, self.exponent)) + factor = pow(self.BASE, self.exponent - new_exp) + new_enc = self.encoding * factor % self.public_key.n + return self.__class__(self.public_key, new_enc, new_exp) \ No newline at end of file diff --git a/note.txt b/note.txt new file mode 100644 index 0000000..dda0dc5 --- /dev/null +++ b/note.txt @@ -0,0 +1,10 @@ +编写的sql格式硬性规定: +含有from,多个对象需要用,分割 + +要先定好测试案例,sql语句,然后才方便用if/else,跑结果 + +中央人民医院 +xx阳光社区诊所 +xx大学附属医院 + +select count(*) from xx阳光社区诊所, xx大学附属医院 where diag = "cancer"; \ No newline at end of file diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..439546a --- /dev/null +++ b/requirements.txt @@ -0,0 +1,2 @@ +pymysql +wxPython \ No newline at end of file diff --git a/server.py b/server.py new file mode 100644 index 0000000..5b54649 --- /dev/null +++ b/server.py @@ -0,0 +1,562 @@ +import socket +import time +import pickle +import wx # pylint: disable=e0401 # type: ignore +import CoreAlgorithm +import threading + +import DataSearch +import server_demo + +sleep_time = 0.2 + + +# 审批回执类消息head:01001001 +# sql密文类消息head:01001010 +# 元数据类消息head:01001100 + + +# 用于生成本地公钥和私钥信息 +def generate_key_pair_data(): + public_key_data, private_key_data = CoreAlgorithm.generate_paillier_keypair() + return private_key_data, public_key_data + + +# 从公钥证书中提取公钥信息 +def get_public_key_data(message): + message = eval(message.replace("\n", "")) + public_key = ( + message["public_key"] + .replace("-----BEGIN PUBLIC KEY-----", "") + .replace("-----END PUBLIC KEY-----", "") + ) # 分割得到公钥 + public_key_bytes = bytes.fromhex(public_key) + public_key_data = pickle.loads(public_key_bytes) + return public_key_data + + +# 用公钥为本地生成数字证书 +def get_certificate(temp_public_key, cert_name): + public_key_str = "\n".join( + temp_public_key[i : i + 60] for i in range(0, len(temp_public_key), 60) + ) + pack_public_key = ( + "-----BEGIN PUBLIC KEY-----\n" + public_key_str + "\n-----END PUBLIC KEY-----\n" + ) + cert = {"public_key": pack_public_key, "name": cert_name} + return cert + + +# 加密字符串 +def str_to_encrypt(message, public_data): + # str 转 int + if message.isdigit(): + int_message = int(message) + else: + int_message = int.from_bytes(message.encode(), "big") + enc_message = public_data.encrypt(int_message) + print("int_message", int_message) + return enc_message + + +class MyServer(server_demo.MyFrame): + def __init__(self, parent): + server_demo.MyFrame.__init__(self, parent) + # 生成私钥和公钥信息 + self.private_key_data, self.public_key_data = generate_key_pair_data() + # 生成私钥和公钥字符串 + self.private_key, self.public_key = self.generate_key_pair() + # 获取数字证书 + self.certificate = get_certificate(self.public_key, "安全多方服务器") + # 初始化当前sql + self.sql = "" + # 初始化sql拆分对象 + self.divide_sqls = [] + # 初始化sql拆分数据源 + self.divide_providers = [] + # 记录属于同一个请求的元数据密文 + self.datas = [] + # 初始化数据查询方的公钥证书为str,在发来过后为其赋值 + self.search_cert = "" + # 初始化socket + self.server = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + # 绑定IP地址和端口 + self.server.bind(("localhost", 1111)) + # 设置最大监听数 + self.server.listen(5) + # 设置一个字典,用来保存每一个客户端的连接和身份信息 + self.socket_mapping = {} # temp_socket: [addr, 公钥信息] + # 设置接收的最大字节数 + self.maxSize = 4096 + # 记录调用方地址 + self.source = None + # 记录收集信息的数量 + self.flag = 0 + # 记录需要收集的信息总量 + self.total = 0 + # 保存安全多方计算结果 + self.result = 0 + self.out_look() + # 等待客户端连接 + message_p = "等待客户端连接..." + print(message_p) + self.m_listCtrl3.Append([message_p]) + time.sleep(sleep_time) + + # 添加消息记录列 + def out_look(self): + self.m_listCtrl1.InsertColumn(0, "消息记录") # 为聊天框添加‘消息记录’列 + self.m_listCtrl1.SetColumnWidth(0, 1000) + self.m_listCtrl2.InsertColumn(0, "消息记录") # 为聊天框添加‘消息记录’列 + self.m_listCtrl2.SetColumnWidth(0, 1000) + self.m_listCtrl3.InsertColumn(0, "消息记录") # 为聊天框添加‘消息记录’列 + self.m_listCtrl3.SetColumnWidth(0, 1000) + + # 建立连接,设置线程 + def run(self): + while True: + client_socket, addr = self.server.accept() + # 发送信息,提示客户端已成功连接 + message_p = "与{0}连接成功!".format(addr) + print(message_p) + self.m_listCtrl3.Append([message_p]) + time.sleep(sleep_time) + + # 将客户端socket等信息存入字典 + self.socket_mapping[client_socket] = [] + self.socket_mapping[client_socket].append(addr) + message_p = "安全多方计算平台:单向授权成功!" + client_socket.send(message_p.encode()) + self.m_listCtrl2.Append([message_p]) + time.sleep(sleep_time) + + # 创建线程,负责接收客户端信息并转发给其他客户端 + threading.Thread( + target=self.recv_from_client, args=(client_socket,) + ).start() + + # 产生公私钥字符串 + def generate_key_pair(self): + # Paillier 转 bytes + public_key_bytes = pickle.dumps(self.public_key_data) + private_key_bytes = pickle.dumps(self.private_key_data) + # bytes 转 str + public_key = public_key_bytes.hex() + private_key = private_key_bytes.hex() + return private_key, public_key + + # 接收客户端消息并转发 + def recv_from_client(self, client_socket): # client_socket指的是连接到的端口socket + while True: + message = client_socket.recv(self.maxSize).decode("utf-8") + message_p = "接收到来自{0}的message:\n{1}".format( + self.socket_mapping[client_socket][0], message + ) + print(message_p) + self.m_listCtrl1.Append([message_p]) # 准备文件传输 + if message.startswith("01001001"): + message_p = "正在解析消息内容..." + print(message_p) + self.m_listCtrl3.Append([message_p]) + time.sleep(sleep_time) + # 去掉审批回执的头部 + message = message.split("01001001", 1)[ + 1 + ] # [0]是空字符串,已测试。只切一次是防止密文出现和头部一样的信息被误切。 + # 认证发送者的合法性 + sender = message.split("||")[0] + context = message.split("||")[1] + message_p = "接收到来自{0}的message:\n{1}".format(sender, context) + print(message_p) + self.m_listCtrl1.Append([message_p]) + # time.sleep(sleep_time) + self.flag += 1 + if context == "想得美哦!不给(*^▽^*)": + self.flag = -9999 + elif message.startswith("01001010"): + message_p = "正在解析消息内容..." + print(message_p) + self.m_listCtrl3.Append([message_p]) + # time.sleep(sleep_time) + # 认证发送者的合法性 + if len(self.socket_mapping[client_socket]) > 1: # 如果发送者的公钥信息已被收集 + # 识别明文中一起发送的发送目标 明文应是发送者||发送内容(||时间戳等),对象用socket表示吧... + message = message.split("01001010", 1)[ + 1 + ] # [0]是空字符串,已测试。只切一次是防止密文出现和头部一样的信息被误切。 + # 用发送目标之前给的公钥加密明文,得到密文。 + # 去掉sql密文的头部 + # 使用平台私钥解密消息,获得sql明文 + message_p = "接收到的sql密文:" + message + print(message_p) + self.m_listCtrl1.Append([message_p]) + time.sleep(sleep_time) + int_message = int(message) + # Ciphertext转EncryptedNumber + Encrpt_message = CoreAlgorithm.EncryptedNumber( + self.public_key_data, int_message, exponent=0 + ) + dec_int_message = self.private_key_data.decrypt(Encrpt_message) + dec_message = dec_int_message.to_bytes( + (dec_int_message.bit_length() + 7) // 8, "big" + ).decode() + message_p = "解密后的消息为:" + dec_message + print(message_p) + self.m_listCtrl3.Append([message_p]) + time.sleep(sleep_time) + self.source = dec_message.split("||")[0] + self.sql = dec_message.split("||")[1] + message_p = "收到已授权方的sql:" + self.sql + print(message_p) + self.m_listCtrl1.Append([message_p]) + time.sleep(sleep_time) + message_p = "待处理的sql语句为:" + self.sql + print(message_p) + self.m_listCtrl3.Append([message_p]) + time.sleep(sleep_time) + message_p = "正在拆分sql..." + print(message_p) + self.m_listCtrl3.Append([message_p]) + time.sleep(sleep_time) + self.divide_providers = DataSearch.extract_tables(self.sql) + message_p = "涉及到的数据持有对象有:" + str(self.divide_providers) + print(message_p) + self.m_listCtrl3.Append(([message_p])) + self.divide_sqls = DataSearch.divide_sql(self.sql) + + message_p = "正在分别加密和封装sql..." + print(message_p) + self.m_listCtrl3.Append([message_p]) + time.sleep(sleep_time) + + self.pack_sqls() + message_p = "发送成功!" + print(message_p) + self.m_listCtrl3.Append([message_p]) + time.sleep(sleep_time) + else: + message_p = "非授权对象,禁止访问!" + print(message_p) + self.m_listCtrl3.Append([message_p]) + time.sleep(sleep_time) + # show_list() + + elif message.startswith("01001100"): + message_p = "正在解析消息内容..." + print(message_p) + self.m_listCtrl3.Append([message_p]) + time.sleep(sleep_time) + # 去掉元数据头部 + message = message.split("01001100", 1)[ + 1 + ] # [0]是空字符串,已测试。只切一次是防止密文出现和头部一样的信息被误切。 + # 把元数据存入本地数据库的临时表里,格式:provider + encode_data + # print("message:", message) + int_message = int(message) + message_p = "收到元数据为:{}".format(int_message) + print(message_p) + self.m_listCtrl1.Append([message_p]) + # 根据证书找公钥信息 + search_public_key = ( + self.search_cert["public_key"] + .split("-----END PUBLIC KEY-----")[0] + .split("-----BEGIN PUBLIC KEY-----")[1] + ) + search_public_key = search_public_key.replace("\n", "") + print("提取到的search_public_key:") + print(search_public_key) + + # str转bytes + byte = bytes.fromhex(search_public_key) + # bytes转PaillierPublicKey + search_public_key_data = pickle.loads(byte) + print("对应的search_public_key_data:") + print(search_public_key_data) + # int密 -- EncryptedNumber密 + Encrpt_message = CoreAlgorithm.EncryptedNumber( + search_public_key_data, int_message, exponent=0 + ) + self.datas.append(Encrpt_message) + # Ciphertext转EncryptedNumber + # print("int_message:", int_message) + # Encrpt_message = CoreAlgorithm.EncryptedNumber(self.public_key_data, int_message, exponent=0) + # print("Enc:", Encrpt_message) + # dec_int_message = self.private_key_data.decrypt(Encrpt_message) + # print("dec:", dec_int_message) + # dec_message = dec_int_message.to_bytes((dec_int_message.bit_length() + 7) // 8, 'big').decode() + # print("解密后的消息为:", dec_message) # 已测试,说明平台不可解密元数据 + self.safe_calculate() + + elif message == "请发送平台的完整公钥证书": + message_p = "正在发送证书..." + print(message_p) + self.m_listCtrl3.Append([message_p]) + time.sleep(sleep_time) + + message_p = str(self.certificate) + client_socket.send(message_p.encode()) # 二进制传输 + self.m_listCtrl2.Append([message_p]) + time.sleep(sleep_time) + + message_p = "发送完成!" + print(message_p) + self.m_listCtrl3.Append([message_p]) + time.sleep(sleep_time) + elif message.startswith("{'public_key':"): + print(message) + print(self.socket_mapping) + print(get_public_key_data(message)) + self.socket_mapping[client_socket].append( + get_public_key_data(message) + ) # 绑定端口与公钥信息的关系 + print(self.socket_mapping) + cert = eval(message) + print(cert, type(cert)) # 字典型 + if cert["name"].startswith("<数据查询方>"): + self.search_cert = cert # 字典型 + self.socket_mapping[client_socket].append(cert["name"]) # 绑定端口与用户身份的关系 + message_p = "接收到一则公钥证书:" + print(message_p) + self.m_listCtrl1.Append([message_p]) + time.sleep(sleep_time) + message_p = "" + for key, value in cert.items(): + if isinstance(value, bytes): + value = value.decode() + print(key, ":", value) + message_p = key + ":\n" + value + print(message_p) + self.m_listCtrl1.Append([message_p]) + time.sleep(sleep_time) + + message_p = "发送完成!" + print(message_p) + self.m_listCtrl3.Append([message_p]) + time.sleep(sleep_time) + + message_p = "安全多方计算平台:双向授权成功!" + print(message_p) + self.m_listCtrl2.Append([message_p]) + client_socket.send(message_p.encode("utf-8")) # 二进制传输 + time.sleep(sleep_time) + + message_p = "使用对象表已更新:" + print(message_p) + self.m_listCtrl3.Append([message_p]) + time.sleep(sleep_time) + print(self.socket_mapping) + self.m_listCtrl3.Append([self.socket_mapping]) + elif message == "请求查询方信息!": + if str(self.search_cert) != "": + message_p = "正在发送提供方的证书..." + print(message_p) + self.m_listCtrl3.Append([message_p]) + time.sleep(sleep_time) + + message_p = str(self.search_cert) + client_socket.send(message_p.encode()) # 二进制传输 + self.m_listCtrl2.Append([message_p]) + time.sleep(sleep_time) + else: + client_socket.send(" ".encode()) # 二进制传输 + + for key, value in self.socket_mapping.items(): + message_p = str(key) + ":" + str(value) + "\n" + print(message_p) + self.m_listCtrl3.Append([message_p]) + elif message == "": + pass + elif message == "认证成功!": + pass + else: + message_p = "Message:" + message + print(message_p) + self.m_listCtrl1.Append([message_p]) + time.sleep(sleep_time) + message_p = "错误的消息格式,丢弃!" + print(message_p) + self.m_listCtrl3.Append([message_p]) + time.sleep(sleep_time) + + # 打印公钥 + def print_public_key(self, event): + message_p = "本地公钥信息为:" + print(message_p) + self.m_listCtrl3.Append([message_p]) + + message_p = self.public_key_data + print(message_p) + self.m_listCtrl3.Append([message_p]) + + message_p = "打印公钥如下:" + print(message_p) + self.m_listCtrl3.Append([message_p]) + + public_key_str = "\n".join( + self.public_key[i : i + 60] for i in range(0, len(self.public_key), 60) + ) + pack_public_key = ( + "-----BEGIN PUBLIC KEY-----\n" + + public_key_str + + "\n-----END PUBLIC KEY-----\n" + ) + message_p = pack_public_key + print(message_p) + message = message_p.split("\n") # 设置打印格式,因为显示窗打印不了\n + for i in range(len(message)): + self.m_listCtrl3.Append([message[i]]) + # show_list() + + # 打印私钥 + def print_private_key(self, event): + message_p = "本地私钥信息为:" + print(message_p) + self.m_listCtrl3.Append([message_p]) + + message_p = self.private_key_data + print(message_p) + self.m_listCtrl3.Append([message_p]) + + private_key_str = "\n".join( + self.private_key[i : i + 60] for i in range(0, len(self.public_key), 60) + ) + pack_private_key = ( + "-----BEGIN PRIVATE KEY-----\n" + + private_key_str + + "\n-----END PRIVATE KEY-----\n" + ) + + message_p = "打印私钥如下:" + print(message_p) + self.m_listCtrl3.Append([message_p]) + + message_p = pack_private_key + print(message_p) + message = message_p.split("\n") # 设置打印格式,因为显示窗打印不了\n + for i in range(len(message)): + self.m_listCtrl3.Append([message[i]]) + + # 打印证书 + def print_certificate(self, event): + message_p = "本地公钥证书为:" + print(message_p) + self.m_listCtrl3.Append([message_p]) + + for key, value in self.certificate.items(): + message_p = key + ":" + print(message_p) + self.m_listCtrl3.Append([message_p]) + + if key == "public_key": + value = value.split("\n") + for i in range(len(value)): + self.m_listCtrl3.Append([value[i]]) + else: + self.m_listCtrl3.Append([value]) + + # show_list() + + # 加密封装sqls并发送 + def pack_sqls(self): + for key, value in self.socket_mapping.items(): + for i in range(len(self.divide_providers)): + if ( + self.divide_providers[i] in value[2] + ): # eg: value[2] == "<数据提供方>风舱医院" + for j in range(len(self.divide_sqls)): + if ( + self.divide_providers[i] in self.divide_sqls[j] + ): # 如果发送目标和信息匹配) + sql = ( + str(self.source) + + "||" + + str(key.getsockname()) + + "||" + + self.divide_sqls[i] + ) + print(sql) + int_enc_sql = str_to_encrypt( + sql, value[1] + ).ciphertext() # 用接收者的公钥加密消息 + message_p = "01001010" + str(int_enc_sql) + key.send(message_p.encode()) + self.m_listCtrl2.Append([message_p]) + message_p = "已将消息{0}发送给{1},其地址为{2}".format( + self.divide_sqls[j], + self.divide_providers[i], + key.getsockname(), + ) + print(message_p) + self.m_listCtrl3.Append([message_p]) + + # 安全算法 + def safe_calculate(self): + self.total = len(self.divide_providers) + if self.flag == self.total: + message_p = "正在进行安全多方计算分析..." + print(message_p) + self.m_listCtrl3.Append([message_p]) + time.sleep(sleep_time) + # #################安全多方计算分析过程############## + if "select count(*) from xx阳光社区诊所, xx大学附属医院" in self.sql: + print(self.datas) + for x in self.datas: + self.result = self.result + x + # EncryptedNumber密 -- int密 + self.result = self.result.ciphertext() + # int密 -- str密 + self.result = str(self.result) + message = "分析成功!" + print(message) + self.m_listCtrl3.Append([message]) + # EncryptedNumber 转 int + # self.result = self.result.ciphertext() + message = "结果:" + str(self.result) + print(message) + self.m_listCtrl3.Append([message]) + time.sleep(sleep_time) + + message_p = "正在发送结果给申请人..." + print(message_p) + self.m_listCtrl3.Append([message_p]) + time.sleep(sleep_time) + + for key, value in self.socket_mapping.items(): + if value[2].startswith("<数据查询方>"): + key.send(message.encode()) + # 重置参数 + self.total = 0 + self.flag = 0 + self.result = 0 + self.datas = [] + elif self.flag < 0: + message_p = "结果:已有数据持有方拒绝了提供消息的请求,安全分析无法进行,分析失败!" + print(message_p) + self.m_listCtrl3.Append([message_p]) + time.sleep(sleep_time) + for key, value in self.socket_mapping.items(): + if value[2].startswith("<数据查询方>"): + key.send(message_p.encode()) + # 重置参数 + self.total = 0 + self.flag = 0 + else: + message_p = "结果:正在等待接收其他信息..." + print(message_p) + self.m_listCtrl3.Append([message_p]) + time.sleep(sleep_time) + for key, value in self.socket_mapping.items(): + if value[2].startswith("<数据查询方>"): + key.send(message_p.encode()) + message_p = "发送完成!" + print(message_p) + self.m_listCtrl3.Append([message_p]) + time.sleep(sleep_time) + + +# ----------------------------------------------------主程序---------------------------------------------------- +app = wx.App() +frame = MyServer(None) +frame.Show(True) # 展示登录页面 +threading.Thread(target=frame.run).start() # 在新线程中运行服务器 +app.MainLoop() diff --git a/server_demo.py b/server_demo.py new file mode 100644 index 0000000..8be53fa --- /dev/null +++ b/server_demo.py @@ -0,0 +1,262 @@ +import wx # pylint: disable=e0401 # type: ignore +import wx.xrc # pylint: disable=e0401 # type: ignore + + +class MyFrame(wx.Frame): + def __init__(self, parent): + wx.Frame.__init__( + self, + parent, + id=wx.ID_ANY, + title="安全多方服务器平台", + pos=wx.DefaultPosition, + size=wx.Size(444, 749), + style=wx.DEFAULT_FRAME_STYLE | wx.TAB_TRAVERSAL, + ) + + self.SetSizeHints(wx.DefaultSize, wx.DefaultSize) + self.SetFont(wx.Font(5, 70, 90, 90, False, "宋体")) + self.SetForegroundColour(wx.SystemSettings.GetColour(wx.SYS_COLOUR_WINDOW)) + self.SetBackgroundColour( + wx.SystemSettings.GetColour(wx.SYS_COLOUR_HIGHLIGHTTEXT) + ) + + fgSizer1 = wx.FlexGridSizer(0, 2, 0, 0) + fgSizer1.SetFlexibleDirection(wx.BOTH) + fgSizer1.SetNonFlexibleGrowMode(wx.FLEX_GROWMODE_SPECIFIED) + + bSizer11 = wx.BoxSizer(wx.VERTICAL) + + self.m_bitmap11 = wx.StaticBitmap( + self, wx.ID_ANY, wx.NullBitmap, wx.DefaultPosition, wx.Size(70, 30), 0 + ) + bSizer11.Add(self.m_bitmap11, 0, wx.ALL, 5) + + self.m_staticline211 = wx.StaticLine( + self, wx.ID_ANY, wx.DefaultPosition, wx.Size(-1, 2), wx.LI_HORIZONTAL + ) + self.m_staticline211.SetForegroundColour( + wx.SystemSettings.GetColour(wx.SYS_COLOUR_INACTIVECAPTION) + ) + self.m_staticline211.SetMaxSize(wx.Size(80, -1)) + + bSizer11.Add(self.m_staticline211, 0, wx.EXPAND | wx.ALL, 5) + + self.m_staticText1 = wx.StaticText( + self, wx.ID_ANY, "接收记录", wx.DefaultPosition, wx.Size(-1, 30), 0 + ) + self.m_staticText1.Wrap(-1) + self.m_staticText1.SetFont(wx.Font(11, 70, 90, 90, False, "宋体")) + self.m_staticText1.SetForegroundColour( + wx.SystemSettings.GetColour(wx.SYS_COLOUR_WINDOWTEXT) + ) + + bSizer11.Add(self.m_staticText1, 0, wx.ALL, 5) + + self.m_bitmap1 = wx.StaticBitmap( + self, wx.ID_ANY, wx.NullBitmap, wx.DefaultPosition, wx.Size(70, 111), 0 + ) + bSizer11.Add(self.m_bitmap1, 0, wx.ALL, 5) + + self.m_staticline21 = wx.StaticLine( + self, wx.ID_ANY, wx.DefaultPosition, wx.Size(-1, 2), wx.LI_HORIZONTAL + ) + self.m_staticline21.SetForegroundColour( + wx.SystemSettings.GetColour(wx.SYS_COLOUR_INACTIVECAPTION) + ) + self.m_staticline21.SetMaxSize(wx.Size(80, -1)) + + bSizer11.Add(self.m_staticline21, 0, wx.EXPAND | wx.ALL, 5) + + self.m_staticText11 = wx.StaticText( + self, wx.ID_ANY, "发送记录", wx.DefaultPosition, wx.Size(-1, 30), 0 + ) + self.m_staticText11.Wrap(-1) + self.m_staticText11.SetFont(wx.Font(11, 70, 90, 90, False, "宋体")) + self.m_staticText11.SetForegroundColour( + wx.SystemSettings.GetColour(wx.SYS_COLOUR_WINDOWTEXT) + ) + + bSizer11.Add(self.m_staticText11, 0, wx.ALL, 5) + + self.m_bitmap12 = wx.StaticBitmap( + self, wx.ID_ANY, wx.NullBitmap, wx.DefaultPosition, wx.Size(70, 111), 0 + ) + bSizer11.Add(self.m_bitmap12, 0, wx.ALL, 5) + + self.m_staticline212 = wx.StaticLine( + self, wx.ID_ANY, wx.DefaultPosition, wx.Size(-1, 2), wx.LI_HORIZONTAL + ) + self.m_staticline212.SetForegroundColour( + wx.SystemSettings.GetColour(wx.SYS_COLOUR_INACTIVECAPTION) + ) + self.m_staticline212.SetMaxSize(wx.Size(80, -1)) + + bSizer11.Add(self.m_staticline212, 0, wx.EXPAND | wx.ALL, 5) + + self.m_staticText111 = wx.StaticText( + self, wx.ID_ANY, "当前状态", wx.DefaultPosition, wx.Size(-1, 30), 0 + ) + self.m_staticText111.Wrap(-1) + self.m_staticText111.SetFont(wx.Font(11, 70, 90, 90, False, "宋体")) + self.m_staticText111.SetForegroundColour( + wx.SystemSettings.GetColour(wx.SYS_COLOUR_WINDOWTEXT) + ) + + bSizer11.Add(self.m_staticText111, 0, wx.ALL, 5) + + fgSizer1.Add(bSizer11, 1, wx.EXPAND, 5) + + bSizer1 = wx.BoxSizer(wx.HORIZONTAL) + + bSizer2 = wx.BoxSizer(wx.VERTICAL) + + self.m_staticText = wx.StaticText( + self, wx.ID_ANY, "安全多方平台服务器", wx.DefaultPosition, wx.Size(-1, 30), 0 + ) + self.m_staticText.Wrap(-1) + self.m_staticText.SetFont(wx.Font(16, 70, 90, 92, False, "宋体")) + self.m_staticText.SetForegroundColour( + wx.SystemSettings.GetColour(wx.SYS_COLOUR_WINDOWTEXT) + ) + + bSizer2.Add(self.m_staticText, 0, wx.ALL, 5) + + self.m_staticline1 = wx.StaticLine( + self, wx.ID_ANY, wx.DefaultPosition, wx.Size(1, 2), wx.LI_HORIZONTAL + ) + self.m_staticline1.SetForegroundColour( + wx.SystemSettings.GetColour(wx.SYS_COLOUR_INACTIVECAPTION) + ) + + bSizer2.Add(self.m_staticline1, 0, wx.EXPAND | wx.ALL, 5) + + self.m_listCtrl1 = wx.ListCtrl( + self, wx.ID_ANY, wx.DefaultPosition, wx.Size(360, 150), wx.LC_REPORT + ) + self.m_listCtrl1.SetFont(wx.Font(9, 70, 90, 90, False, "宋体")) + self.m_listCtrl1.SetForegroundColour( + wx.SystemSettings.GetColour(wx.SYS_COLOUR_WINDOWTEXT) + ) + + bSizer2.Add(self.m_listCtrl1, 0, wx.ALL, 5) + + self.m_staticline2 = wx.StaticLine( + self, wx.ID_ANY, wx.DefaultPosition, wx.Size(-1, 2), wx.LI_HORIZONTAL + ) + self.m_staticline2.SetForegroundColour( + wx.SystemSettings.GetColour(wx.SYS_COLOUR_INACTIVECAPTION) + ) + + bSizer2.Add(self.m_staticline2, 0, wx.EXPAND | wx.ALL, 5) + + self.m_listCtrl2 = wx.ListCtrl( + self, wx.ID_ANY, wx.DefaultPosition, wx.Size(360, 150), wx.LC_REPORT + ) + self.m_listCtrl2.SetFont(wx.Font(9, 70, 90, 90, False, "宋体")) + self.m_listCtrl2.SetForegroundColour( + wx.SystemSettings.GetColour(wx.SYS_COLOUR_WINDOWTEXT) + ) + + bSizer2.Add(self.m_listCtrl2, 0, wx.ALL, 5) + + self.m_staticline22 = wx.StaticLine( + self, wx.ID_ANY, wx.DefaultPosition, wx.Size(-1, 2), wx.LI_HORIZONTAL + ) + self.m_staticline22.SetForegroundColour( + wx.SystemSettings.GetColour(wx.SYS_COLOUR_INACTIVECAPTION) + ) + + bSizer2.Add(self.m_staticline22, 0, wx.EXPAND | wx.ALL, 5) + + self.m_listCtrl3 = wx.ListCtrl( + self, wx.ID_ANY, wx.DefaultPosition, wx.Size(360, 150), wx.LC_REPORT + ) + self.m_listCtrl3.SetFont(wx.Font(9, 70, 90, 90, False, "宋体")) + self.m_listCtrl3.SetForegroundColour( + wx.SystemSettings.GetColour(wx.SYS_COLOUR_WINDOWTEXT) + ) + + bSizer2.Add(self.m_listCtrl3, 0, wx.ALL, 5) + + self.m_staticline221 = wx.StaticLine( + self, wx.ID_ANY, wx.DefaultPosition, wx.Size(-1, 2), wx.LI_HORIZONTAL + ) + self.m_staticline221.SetForegroundColour( + wx.SystemSettings.GetColour(wx.SYS_COLOUR_INACTIVECAPTION) + ) + + bSizer2.Add(self.m_staticline221, 0, wx.EXPAND | wx.ALL, 5) + + bSizer12 = wx.BoxSizer(wx.VERTICAL) + + bSizer102 = wx.BoxSizer(wx.HORIZONTAL) + + self.m_button31 = wx.Button( + self, wx.ID_ANY, "显示本地公钥", wx.DefaultPosition, wx.Size(300, 30), 0 + ) + self.m_button31.SetFont(wx.Font(11, 70, 90, 90, False, wx.EmptyString)) + self.m_button31.SetForegroundColour( + wx.SystemSettings.GetColour(wx.SYS_COLOUR_WINDOWTEXT) + ) + + bSizer102.Add(self.m_button31, 0, wx.ALIGN_CENTER | wx.ALL, 5) + + bSizer12.Add(bSizer102, 1, wx.EXPAND, 5) + + bSizer1012 = wx.BoxSizer(wx.HORIZONTAL) + + self.m_button41 = wx.Button( + self, wx.ID_ANY, "显示本地私钥", wx.DefaultPosition, wx.Size(300, 30), 0 + ) + self.m_button41.SetFont(wx.Font(11, 70, 90, 90, False, wx.EmptyString)) + self.m_button41.SetForegroundColour( + wx.SystemSettings.GetColour(wx.SYS_COLOUR_WINDOWTEXT) + ) + + bSizer1012.Add(self.m_button41, 0, wx.ALIGN_CENTER | wx.ALL, 5) + + bSizer12.Add(bSizer1012, 1, wx.EXPAND, 5) + + bSizer10111 = wx.BoxSizer(wx.HORIZONTAL) + + self.m_button51 = wx.Button( + self, wx.ID_ANY, "打印本地证书", wx.DefaultPosition, wx.Size(300, 30), 0 + ) + self.m_button51.SetFont(wx.Font(11, 70, 90, 90, False, wx.EmptyString)) + self.m_button51.SetForegroundColour( + wx.SystemSettings.GetColour(wx.SYS_COLOUR_WINDOWTEXT) + ) + + bSizer10111.Add(self.m_button51, 0, wx.ALIGN_CENTER | wx.ALL, 5) + + bSizer12.Add(bSizer10111, 1, wx.EXPAND, 5) + + bSizer2.Add(bSizer12, 1, wx.EXPAND, 5) + + bSizer1.Add(bSizer2, 1, wx.EXPAND, 5) + + fgSizer1.Add(bSizer1, 1, wx.EXPAND, 5) + + self.SetSizer(fgSizer1) + self.Layout() + + self.Centre(wx.BOTH) + + # Connect Events + self.m_button31.Bind(wx.EVT_BUTTON, self.print_public_key) + self.m_button41.Bind(wx.EVT_BUTTON, self.print_private_key) + self.m_button51.Bind(wx.EVT_BUTTON, self.print_certificate) + + def __del__(self): + pass + + # Virtual event handlers, override them in your derived class + def print_public_key(self, event): + event.Skip() + + def print_private_key(self, event): + event.Skip() + + def print_certificate(self, event): + event.Skip() diff --git a/util.py b/util.py new file mode 100644 index 0000000..9d1c7ad --- /dev/null +++ b/util.py @@ -0,0 +1,2286 @@ +import os +import random +from base64 import urlsafe_b64encode, urlsafe_b64decode +from binascii import hexlify, unhexlify + +try: + import gmpy2 + + HAVE_GMP = True +except ImportError: + HAVE_GMP = False + +try: + from Crypto.Util import number + + HAVE_CRYPTO = True +except ImportError: + HAVE_CRYPTO = False + +# GMP's powmod has greater overhead than Python's pow, but is faster. +# From a quick experiment on our machine, this seems to be the break even: +_USE_MOD_FROM_GMP_SIZE = 1 << (8 * 2) + + +def powmod(a, b, c): + """ + Uses GMP, if available, to do a^b mod c where a, b, c + are integers. + + :return int: (a ** b) % c + """ + if a == 1: + return 1 + if not HAVE_GMP or max(a, b, c) < _USE_MOD_FROM_GMP_SIZE: + return pow(a, b, c) + else: + return int(gmpy2.powmod(a, b, c)) # pylint: disable=e1101 + + +def extended_euclidean_algorithm(a, b): + """Extended Euclidean algorithm + + Returns r, s, t such that r = s*a + t*b and r is gcd(a, b) + + See + """ + r0, r1 = a, b + s0, s1 = 1, 0 + t0, t1 = 0, 1 + while r1 != 0: + q = r0 // r1 + r0, r1 = r1, r0 - q * r1 + s0, s1 = s1, s0 - q * s1 + t0, t1 = t1, t0 - q * t1 + return r0, s0, t0 + + +def invert(a, b): + """ + The multiplicitive inverse of a in the integers modulo b. + + :return int: x, where a * x == 1 mod b + """ + if HAVE_GMP: + s = int(gmpy2.invert(a, b)) # pylint: disable=e1101 + # according to documentation, gmpy2.invert might return 0 on + # non-invertible element, although it seems to actually raise an + # exception; for consistency, we always raise the exception + if s == 0: + raise ZeroDivisionError("invert() no inverse exists") + return s + else: + r, s, _ = extended_euclidean_algorithm(a, b) + if r != 1: + raise ZeroDivisionError("invert() no inverse exists") + return s % b + + +def getprimeover(N): + """Return a random N-bit prime number using the System's best + Cryptographic random source. + + Use GMP if available, otherwise fallback to PyCrypto + """ + if HAVE_GMP: + randfunc = random.SystemRandom() + r = gmpy2.mpz(randfunc.getrandbits(N)) # pylint: disable=e1101 + r = gmpy2.bit_set(r, N - 1) # pylint: disable=e1101 + return int(gmpy2.next_prime(r)) # pylint: disable=e1101 + elif HAVE_CRYPTO: + return number.getPrime(N, os.urandom) + else: + randfunc = random.SystemRandom() + n = randfunc.randrange(2 ** (N - 1), 2**N) | 1 + while not is_prime(n): + n += 2 + return n + + +def isqrt(N): + """returns the integer square root of N""" + if HAVE_GMP: + return int(gmpy2.isqrt(N)) # pylint: disable=e1101 + else: + return improved_i_sqrt(N) + + +def improved_i_sqrt(n): + """taken from + http://stackoverflow.com/questions/15390807/integer-square-root-in-python + Thanks, mathmandan""" + assert n >= 0 + if n == 0: + return 0 + i = n.bit_length() >> 1 # i = floor( (1 + floor(log_2(temp_n))) / 2 ) + m = 1 << i # m = 2^i + # + # Fact: (2^(i + 1))^2 > temp_n, so m has at least as many bits + # as the floor of the square root of temp_n. + # + # Proof: (2^(i+1))^2 = 2^(2i + 2) >= 2^(floor(log_2(temp_n)) + 2) + # >= 2^(ceil(log_2(temp_n) + 1) >= 2^(log_2(temp_n) + 1) > 2^(log_2(temp_n)) = temp_n. QED. + # + while (m << i) > n: # (m<>= 1 + i -= 1 + d = n - (m << i) # d = temp_n-m^2 + for k in range(i - 1, -1, -1): + j = 1 << k + new_diff = d - ( + ((m << 1) | j) << k + ) # temp_n-(m+2^k)^2 = temp_n-m^2-2*m*2^k-2^(2k) + if new_diff >= 0: + d = new_diff + m |= j + return m + + +# base64 utils from jwcrypto + + +def base64url_encode(payload): + if not isinstance(payload, bytes): + payload = payload.encode("utf-8") + encode = urlsafe_b64encode(payload) + return encode.decode("utf-8").rstrip("=") + + +def base64url_decode(payload): + l = len(payload) % 4 + if l == 2: + payload += "==" + elif l == 3: + payload += "=" + elif l != 0: + raise ValueError("Invalid base64 string") + return urlsafe_b64decode(payload.encode("utf-8")) + + +def base64_to_int(source): + return int(hexlify(base64url_decode(source)), 16) + + +def int_to_base64(source): + assert source != 0 + I = hex(source).rstrip("L").lstrip("0x") + return base64url_encode(unhexlify((len(I) % 2) * "0" + I)) + + +# prime testing + +first_primes = [ + 2, + 3, + 5, + 7, + 11, + 13, + 17, + 19, + 23, + 29, + 31, + 37, + 41, + 43, + 47, + 53, + 59, + 61, + 67, + 71, + 73, + 79, + 83, + 89, + 97, + 101, + 103, + 107, + 109, + 113, + 127, + 131, + 137, + 139, + 149, + 151, + 157, + 163, + 167, + 173, + 179, + 181, + 191, + 193, + 197, + 199, + 211, + 223, + 227, + 229, + 233, + 239, + 241, + 251, + 257, + 263, + 269, + 271, + 277, + 281, + 283, + 293, + 307, + 311, + 313, + 317, + 331, + 337, + 347, + 349, + 353, + 359, + 367, + 373, + 379, + 383, + 389, + 397, + 401, + 409, + 419, + 421, + 431, + 433, + 439, + 443, + 449, + 457, + 461, + 463, + 467, + 479, + 487, + 491, + 499, + 503, + 509, + 521, + 523, + 541, + 547, + 557, + 563, + 569, + 571, + 577, + 587, + 593, + 599, + 601, + 607, + 613, + 617, + 619, + 631, + 641, + 643, + 647, + 653, + 659, + 661, + 673, + 677, + 683, + 691, + 701, + 709, + 719, + 727, + 733, + 739, + 743, + 751, + 757, + 761, + 769, + 773, + 787, + 797, + 809, + 811, + 821, + 823, + 827, + 829, + 839, + 853, + 857, + 859, + 863, + 877, + 881, + 883, + 887, + 907, + 911, + 919, + 929, + 937, + 941, + 947, + 953, + 967, + 971, + 977, + 983, + 991, + 997, + 1009, + 1013, + 1019, + 1021, + 1031, + 1033, + 1039, + 1049, + 1051, + 1061, + 1063, + 1069, + 1087, + 1091, + 1093, + 1097, + 1103, + 1109, + 1117, + 1123, + 1129, + 1151, + 1153, + 1163, + 1171, + 1181, + 1187, + 1193, + 1201, + 1213, + 1217, + 1223, + 1229, + 1231, + 1237, + 1249, + 1259, + 1277, + 1279, + 1283, + 1289, + 1291, + 1297, + 1301, + 1303, + 1307, + 1319, + 1321, + 1327, + 1361, + 1367, + 1373, + 1381, + 1399, + 1409, + 1423, + 1427, + 1429, + 1433, + 1439, + 1447, + 1451, + 1453, + 1459, + 1471, + 1481, + 1483, + 1487, + 1489, + 1493, + 1499, + 1511, + 1523, + 1531, + 1543, + 1549, + 1553, + 1559, + 1567, + 1571, + 1579, + 1583, + 1597, + 1601, + 1607, + 1609, + 1613, + 1619, + 1621, + 1627, + 1637, + 1657, + 1663, + 1667, + 1669, + 1693, + 1697, + 1699, + 1709, + 1721, + 1723, + 1733, + 1741, + 1747, + 1753, + 1759, + 1777, + 1783, + 1787, + 1789, + 1801, + 1811, + 1823, + 1831, + 1847, + 1861, + 1867, + 1871, + 1873, + 1877, + 1879, + 1889, + 1901, + 1907, + 1913, + 1931, + 1933, + 1949, + 1951, + 1973, + 1979, + 1987, + 1993, + 1997, + 1999, + 2003, + 2011, + 2017, + 2027, + 2029, + 2039, + 2053, + 2063, + 2069, + 2081, + 2083, + 2087, + 2089, + 2099, + 2111, + 2113, + 2129, + 2131, + 2137, + 2141, + 2143, + 2153, + 2161, + 2179, + 2203, + 2207, + 2213, + 2221, + 2237, + 2239, + 2243, + 2251, + 2267, + 2269, + 2273, + 2281, + 2287, + 2293, + 2297, + 2309, + 2311, + 2333, + 2339, + 2341, + 2347, + 2351, + 2357, + 2371, + 2377, + 2381, + 2383, + 2389, + 2393, + 2399, + 2411, + 2417, + 2423, + 2437, + 2441, + 2447, + 2459, + 2467, + 2473, + 2477, + 2503, + 2521, + 2531, + 2539, + 2543, + 2549, + 2551, + 2557, + 2579, + 2591, + 2593, + 2609, + 2617, + 2621, + 2633, + 2647, + 2657, + 2659, + 2663, + 2671, + 2677, + 2683, + 2687, + 2689, + 2693, + 2699, + 2707, + 2711, + 2713, + 2719, + 2729, + 2731, + 2741, + 2749, + 2753, + 2767, + 2777, + 2789, + 2791, + 2797, + 2801, + 2803, + 2819, + 2833, + 2837, + 2843, + 2851, + 2857, + 2861, + 2879, + 2887, + 2897, + 2903, + 2909, + 2917, + 2927, + 2939, + 2953, + 2957, + 2963, + 2969, + 2971, + 2999, + 3001, + 3011, + 3019, + 3023, + 3037, + 3041, + 3049, + 3061, + 3067, + 3079, + 3083, + 3089, + 3109, + 3119, + 3121, + 3137, + 3163, + 3167, + 3169, + 3181, + 3187, + 3191, + 3203, + 3209, + 3217, + 3221, + 3229, + 3251, + 3253, + 3257, + 3259, + 3271, + 3299, + 3301, + 3307, + 3313, + 3319, + 3323, + 3329, + 3331, + 3343, + 3347, + 3359, + 3361, + 3371, + 3373, + 3389, + 3391, + 3407, + 3413, + 3433, + 3449, + 3457, + 3461, + 3463, + 3467, + 3469, + 3491, + 3499, + 3511, + 3517, + 3527, + 3529, + 3533, + 3539, + 3541, + 3547, + 3557, + 3559, + 3571, + 3581, + 3583, + 3593, + 3607, + 3613, + 3617, + 3623, + 3631, + 3637, + 3643, + 3659, + 3671, + 3673, + 3677, + 3691, + 3697, + 3701, + 3709, + 3719, + 3727, + 3733, + 3739, + 3761, + 3767, + 3769, + 3779, + 3793, + 3797, + 3803, + 3821, + 3823, + 3833, + 3847, + 3851, + 3853, + 3863, + 3877, + 3881, + 3889, + 3907, + 3911, + 3917, + 3919, + 3923, + 3929, + 3931, + 3943, + 3947, + 3967, + 3989, + 4001, + 4003, + 4007, + 4013, + 4019, + 4021, + 4027, + 4049, + 4051, + 4057, + 4073, + 4079, + 4091, + 4093, + 4099, + 4111, + 4127, + 4129, + 4133, + 4139, + 4153, + 4157, + 4159, + 4177, + 4201, + 4211, + 4217, + 4219, + 4229, + 4231, + 4241, + 4243, + 4253, + 4259, + 4261, + 4271, + 4273, + 4283, + 4289, + 4297, + 4327, + 4337, + 4339, + 4349, + 4357, + 4363, + 4373, + 4391, + 4397, + 4409, + 4421, + 4423, + 4441, + 4447, + 4451, + 4457, + 4463, + 4481, + 4483, + 4493, + 4507, + 4513, + 4517, + 4519, + 4523, + 4547, + 4549, + 4561, + 4567, + 4583, + 4591, + 4597, + 4603, + 4621, + 4637, + 4639, + 4643, + 4649, + 4651, + 4657, + 4663, + 4673, + 4679, + 4691, + 4703, + 4721, + 4723, + 4729, + 4733, + 4751, + 4759, + 4783, + 4787, + 4789, + 4793, + 4799, + 4801, + 4813, + 4817, + 4831, + 4861, + 4871, + 4877, + 4889, + 4903, + 4909, + 4919, + 4931, + 4933, + 4937, + 4943, + 4951, + 4957, + 4967, + 4969, + 4973, + 4987, + 4993, + 4999, + 5003, + 5009, + 5011, + 5021, + 5023, + 5039, + 5051, + 5059, + 5077, + 5081, + 5087, + 5099, + 5101, + 5107, + 5113, + 5119, + 5147, + 5153, + 5167, + 5171, + 5179, + 5189, + 5197, + 5209, + 5227, + 5231, + 5233, + 5237, + 5261, + 5273, + 5279, + 5281, + 5297, + 5303, + 5309, + 5323, + 5333, + 5347, + 5351, + 5381, + 5387, + 5393, + 5399, + 5407, + 5413, + 5417, + 5419, + 5431, + 5437, + 5441, + 5443, + 5449, + 5471, + 5477, + 5479, + 5483, + 5501, + 5503, + 5507, + 5519, + 5521, + 5527, + 5531, + 5557, + 5563, + 5569, + 5573, + 5581, + 5591, + 5623, + 5639, + 5641, + 5647, + 5651, + 5653, + 5657, + 5659, + 5669, + 5683, + 5689, + 5693, + 5701, + 5711, + 5717, + 5737, + 5741, + 5743, + 5749, + 5779, + 5783, + 5791, + 5801, + 5807, + 5813, + 5821, + 5827, + 5839, + 5843, + 5849, + 5851, + 5857, + 5861, + 5867, + 5869, + 5879, + 5881, + 5897, + 5903, + 5923, + 5927, + 5939, + 5953, + 5981, + 5987, + 6007, + 6011, + 6029, + 6037, + 6043, + 6047, + 6053, + 6067, + 6073, + 6079, + 6089, + 6091, + 6101, + 6113, + 6121, + 6131, + 6133, + 6143, + 6151, + 6163, + 6173, + 6197, + 6199, + 6203, + 6211, + 6217, + 6221, + 6229, + 6247, + 6257, + 6263, + 6269, + 6271, + 6277, + 6287, + 6299, + 6301, + 6311, + 6317, + 6323, + 6329, + 6337, + 6343, + 6353, + 6359, + 6361, + 6367, + 6373, + 6379, + 6389, + 6397, + 6421, + 6427, + 6449, + 6451, + 6469, + 6473, + 6481, + 6491, + 6521, + 6529, + 6547, + 6551, + 6553, + 6563, + 6569, + 6571, + 6577, + 6581, + 6599, + 6607, + 6619, + 6637, + 6653, + 6659, + 6661, + 6673, + 6679, + 6689, + 6691, + 6701, + 6703, + 6709, + 6719, + 6733, + 6737, + 6761, + 6763, + 6779, + 6781, + 6791, + 6793, + 6803, + 6823, + 6827, + 6829, + 6833, + 6841, + 6857, + 6863, + 6869, + 6871, + 6883, + 6899, + 6907, + 6911, + 6917, + 6947, + 6949, + 6959, + 6961, + 6967, + 6971, + 6977, + 6983, + 6991, + 6997, + 7001, + 7013, + 7019, + 7027, + 7039, + 7043, + 7057, + 7069, + 7079, + 7103, + 7109, + 7121, + 7127, + 7129, + 7151, + 7159, + 7177, + 7187, + 7193, + 7207, + 7211, + 7213, + 7219, + 7229, + 7237, + 7243, + 7247, + 7253, + 7283, + 7297, + 7307, + 7309, + 7321, + 7331, + 7333, + 7349, + 7351, + 7369, + 7393, + 7411, + 7417, + 7433, + 7451, + 7457, + 7459, + 7477, + 7481, + 7487, + 7489, + 7499, + 7507, + 7517, + 7523, + 7529, + 7537, + 7541, + 7547, + 7549, + 7559, + 7561, + 7573, + 7577, + 7583, + 7589, + 7591, + 7603, + 7607, + 7621, + 7639, + 7643, + 7649, + 7669, + 7673, + 7681, + 7687, + 7691, + 7699, + 7703, + 7717, + 7723, + 7727, + 7741, + 7753, + 7757, + 7759, + 7789, + 7793, + 7817, + 7823, + 7829, + 7841, + 7853, + 7867, + 7873, + 7877, + 7879, + 7883, + 7901, + 7907, + 7919, + 7927, + 7933, + 7937, + 7949, + 7951, + 7963, + 7993, + 8009, + 8011, + 8017, + 8039, + 8053, + 8059, + 8069, + 8081, + 8087, + 8089, + 8093, + 8101, + 8111, + 8117, + 8123, + 8147, + 8161, + 8167, + 8171, + 8179, + 8191, + 8209, + 8219, + 8221, + 8231, + 8233, + 8237, + 8243, + 8263, + 8269, + 8273, + 8287, + 8291, + 8293, + 8297, + 8311, + 8317, + 8329, + 8353, + 8363, + 8369, + 8377, + 8387, + 8389, + 8419, + 8423, + 8429, + 8431, + 8443, + 8447, + 8461, + 8467, + 8501, + 8513, + 8521, + 8527, + 8537, + 8539, + 8543, + 8563, + 8573, + 8581, + 8597, + 8599, + 8609, + 8623, + 8627, + 8629, + 8641, + 8647, + 8663, + 8669, + 8677, + 8681, + 8689, + 8693, + 8699, + 8707, + 8713, + 8719, + 8731, + 8737, + 8741, + 8747, + 8753, + 8761, + 8779, + 8783, + 8803, + 8807, + 8819, + 8821, + 8831, + 8837, + 8839, + 8849, + 8861, + 8863, + 8867, + 8887, + 8893, + 8923, + 8929, + 8933, + 8941, + 8951, + 8963, + 8969, + 8971, + 8999, + 9001, + 9007, + 9011, + 9013, + 9029, + 9041, + 9043, + 9049, + 9059, + 9067, + 9091, + 9103, + 9109, + 9127, + 9133, + 9137, + 9151, + 9157, + 9161, + 9173, + 9181, + 9187, + 9199, + 9203, + 9209, + 9221, + 9227, + 9239, + 9241, + 9257, + 9277, + 9281, + 9283, + 9293, + 9311, + 9319, + 9323, + 9337, + 9341, + 9343, + 9349, + 9371, + 9377, + 9391, + 9397, + 9403, + 9413, + 9419, + 9421, + 9431, + 9433, + 9437, + 9439, + 9461, + 9463, + 9467, + 9473, + 9479, + 9491, + 9497, + 9511, + 9521, + 9533, + 9539, + 9547, + 9551, + 9587, + 9601, + 9613, + 9619, + 9623, + 9629, + 9631, + 9643, + 9649, + 9661, + 9677, + 9679, + 9689, + 9697, + 9719, + 9721, + 9733, + 9739, + 9743, + 9749, + 9767, + 9769, + 9781, + 9787, + 9791, + 9803, + 9811, + 9817, + 9829, + 9833, + 9839, + 9851, + 9857, + 9859, + 9871, + 9883, + 9887, + 9901, + 9907, + 9923, + 9929, + 9931, + 9941, + 9949, + 9967, + 9973, + 10007, + 10009, + 10037, + 10039, + 10061, + 10067, + 10069, + 10079, + 10091, + 10093, + 10099, + 10103, + 10111, + 10133, + 10139, + 10141, + 10151, + 10159, + 10163, + 10169, + 10177, + 10181, + 10193, + 10211, + 10223, + 10243, + 10247, + 10253, + 10259, + 10267, + 10271, + 10273, + 10289, + 10301, + 10303, + 10313, + 10321, + 10331, + 10333, + 10337, + 10343, + 10357, + 10369, + 10391, + 10399, + 10427, + 10429, + 10433, + 10453, + 10457, + 10459, + 10463, + 10477, + 10487, + 10499, + 10501, + 10513, + 10529, + 10531, + 10559, + 10567, + 10589, + 10597, + 10601, + 10607, + 10613, + 10627, + 10631, + 10639, + 10651, + 10657, + 10663, + 10667, + 10687, + 10691, + 10709, + 10711, + 10723, + 10729, + 10733, + 10739, + 10753, + 10771, + 10781, + 10789, + 10799, + 10831, + 10837, + 10847, + 10853, + 10859, + 10861, + 10867, + 10883, + 10889, + 10891, + 10903, + 10909, + 10937, + 10939, + 10949, + 10957, + 10973, + 10979, + 10987, + 10993, + 11003, + 11027, + 11047, + 11057, + 11059, + 11069, + 11071, + 11083, + 11087, + 11093, + 11113, + 11117, + 11119, + 11131, + 11149, + 11159, + 11161, + 11171, + 11173, + 11177, + 11197, + 11213, + 11239, + 11243, + 11251, + 11257, + 11261, + 11273, + 11279, + 11287, + 11299, + 11311, + 11317, + 11321, + 11329, + 11351, + 11353, + 11369, + 11383, + 11393, + 11399, + 11411, + 11423, + 11437, + 11443, + 11447, + 11467, + 11471, + 11483, + 11489, + 11491, + 11497, + 11503, + 11519, + 11527, + 11549, + 11551, + 11579, + 11587, + 11593, + 11597, + 11617, + 11621, + 11633, + 11657, + 11677, + 11681, + 11689, + 11699, + 11701, + 11717, + 11719, + 11731, + 11743, + 11777, + 11779, + 11783, + 11789, + 11801, + 11807, + 11813, + 11821, + 11827, + 11831, + 11833, + 11839, + 11863, + 11867, + 11887, + 11897, + 11903, + 11909, + 11923, + 11927, + 11933, + 11939, + 11941, + 11953, + 11959, + 11969, + 11971, + 11981, + 11987, + 12007, + 12011, + 12037, + 12041, + 12043, + 12049, + 12071, + 12073, + 12097, + 12101, + 12107, + 12109, + 12113, + 12119, + 12143, + 12149, + 12157, + 12161, + 12163, + 12197, + 12203, + 12211, + 12227, + 12239, + 12241, + 12251, + 12253, + 12263, + 12269, + 12277, + 12281, + 12289, + 12301, + 12323, + 12329, + 12343, + 12347, + 12373, + 12377, + 12379, + 12391, + 12401, + 12409, + 12413, + 12421, + 12433, + 12437, + 12451, + 12457, + 12473, + 12479, + 12487, + 12491, + 12497, + 12503, + 12511, + 12517, + 12527, + 12539, + 12541, + 12547, + 12553, + 12569, + 12577, + 12583, + 12589, + 12601, + 12611, + 12613, + 12619, + 12637, + 12641, + 12647, + 12653, + 12659, + 12671, + 12689, + 12697, + 12703, + 12713, + 12721, + 12739, + 12743, + 12757, + 12763, + 12781, + 12791, + 12799, + 12809, + 12821, + 12823, + 12829, + 12841, + 12853, + 12889, + 12893, + 12899, + 12907, + 12911, + 12917, + 12919, + 12923, + 12941, + 12953, + 12959, + 12967, + 12973, + 12979, + 12983, + 13001, + 13003, + 13007, + 13009, + 13033, + 13037, + 13043, + 13049, + 13063, + 13093, + 13099, + 13103, + 13109, + 13121, + 13127, + 13147, + 13151, + 13159, + 13163, + 13171, + 13177, + 13183, + 13187, + 13217, + 13219, + 13229, + 13241, + 13249, + 13259, + 13267, + 13291, + 13297, + 13309, + 13313, + 13327, + 13331, + 13337, + 13339, + 13367, + 13381, + 13397, + 13399, + 13411, + 13417, + 13421, + 13441, + 13451, + 13457, + 13463, + 13469, + 13477, + 13487, + 13499, + 13513, + 13523, + 13537, + 13553, + 13567, + 13577, + 13591, + 13597, + 13613, + 13619, + 13627, + 13633, + 13649, + 13669, + 13679, + 13681, + 13687, + 13691, + 13693, + 13697, + 13709, + 13711, + 13721, + 13723, + 13729, + 13751, + 13757, + 13759, + 13763, + 13781, + 13789, + 13799, + 13807, + 13829, + 13831, + 13841, + 13859, + 13873, + 13877, + 13879, + 13883, + 13901, + 13903, + 13907, + 13913, + 13921, + 13931, + 13933, + 13963, + 13967, + 13997, + 13999, + 14009, + 14011, + 14029, + 14033, + 14051, + 14057, + 14071, + 14081, + 14083, + 14087, + 14107, + 14143, + 14149, + 14153, + 14159, + 14173, + 14177, + 14197, + 14207, + 14221, + 14243, + 14249, + 14251, + 14281, + 14293, + 14303, + 14321, + 14323, + 14327, + 14341, + 14347, + 14369, + 14387, + 14389, + 14401, + 14407, + 14411, + 14419, + 14423, + 14431, + 14437, + 14447, + 14449, + 14461, + 14479, + 14489, + 14503, + 14519, + 14533, + 14537, + 14543, + 14549, + 14551, + 14557, + 14561, + 14563, + 14591, + 14593, + 14621, + 14627, + 14629, + 14633, + 14639, + 14653, + 14657, + 14669, + 14683, + 14699, + 14713, + 14717, + 14723, + 14731, + 14737, + 14741, + 14747, + 14753, + 14759, + 14767, + 14771, + 14779, + 14783, + 14797, + 14813, + 14821, + 14827, + 14831, + 14843, + 14851, + 14867, + 14869, + 14879, + 14887, + 14891, + 14897, + 14923, + 14929, + 14939, + 14947, + 14951, + 14957, + 14969, + 14983, + 15013, + 15017, + 15031, + 15053, + 15061, + 15073, + 15077, + 15083, + 15091, + 15101, + 15107, + 15121, + 15131, + 15137, + 15139, + 15149, + 15161, + 15173, + 15187, + 15193, + 15199, + 15217, + 15227, + 15233, + 15241, + 15259, + 15263, + 15269, + 15271, + 15277, + 15287, + 15289, + 15299, + 15307, + 15313, + 15319, + 15329, + 15331, + 15349, + 15359, + 15361, + 15373, + 15377, + 15383, + 15391, + 15401, + 15413, + 15427, + 15439, + 15443, + 15451, + 15461, + 15467, + 15473, + 15493, + 15497, + 15511, + 15527, + 15541, + 15551, + 15559, + 15569, + 15581, + 15583, + 15601, + 15607, + 15619, + 15629, + 15641, + 15643, + 15647, + 15649, + 15661, + 15667, + 15671, + 15679, + 15683, + 15727, + 15731, + 15733, + 15737, + 15739, + 15749, + 15761, + 15767, + 15773, + 15787, + 15791, + 15797, + 15803, + 15809, + 15817, + 15823, + 15859, + 15877, + 15881, + 15887, + 15889, + 15901, + 15907, + 15913, + 15919, + 15923, + 15937, + 15959, + 15971, + 15973, + 15991, + 16001, + 16007, + 16033, + 16057, + 16061, + 16063, + 16067, + 16069, + 16073, + 16087, + 16091, + 16097, + 16103, + 16111, + 16127, + 16139, + 16141, + 16183, + 16187, + 16189, + 16193, + 16217, + 16223, + 16229, + 16231, + 16249, + 16253, + 16267, + 16273, + 16301, + 16319, + 16333, + 16339, + 16349, + 16361, + 16363, + 16369, + 16381, + 16411, + 16417, + 16421, + 16427, + 16433, + 16447, + 16451, + 16453, + 16477, + 16481, + 16487, + 16493, + 16519, + 16529, + 16547, + 16553, + 16561, + 16567, + 16573, + 16603, + 16607, + 16619, + 16631, + 16633, + 16649, + 16651, + 16657, + 16661, + 16673, + 16691, + 16693, + 16699, + 16703, + 16729, + 16741, + 16747, + 16759, + 16763, + 16787, + 16811, + 16823, + 16829, + 16831, + 16843, + 16871, + 16879, + 16883, + 16889, + 16901, + 16903, + 16921, + 16927, + 16931, + 16937, + 16943, + 16963, + 16979, + 16981, + 16987, + 16993, + 17011, + 17021, + 17027, + 17029, + 17033, + 17041, + 17047, + 17053, + 17077, + 17093, + 17099, + 17107, + 17117, + 17123, + 17137, + 17159, + 17167, + 17183, + 17189, + 17191, + 17203, + 17207, + 17209, + 17231, + 17239, + 17257, + 17291, + 17293, + 17299, + 17317, + 17321, + 17327, + 17333, + 17341, + 17351, + 17359, + 17377, + 17383, + 17387, + 17389, + 17393, + 17401, + 17417, + 17419, + 17431, + 17443, + 17449, + 17467, + 17471, + 17477, + 17483, + 17489, + 17491, + 17497, + 17509, + 17519, + 17539, + 17551, + 17569, + 17573, + 17579, + 17581, + 17597, + 17599, + 17609, + 17623, + 17627, + 17657, + 17659, + 17669, + 17681, + 17683, + 17707, + 17713, + 17729, + 17737, + 17747, + 17749, + 17761, + 17783, + 17789, + 17791, + 17807, + 17827, + 17837, + 17839, + 17851, + 17863, +] + + +def miller_rabin(n, k): + """Run the Miller-Rabin test on temp_n with at most k iterations + + Arguments: + n (int): number whose primality is to be tested + k (int): maximum number of iterations to run + + Returns: + bool: If temp_n is prime, then True is returned. Otherwise, False is + returned, except with probability less than 4**-k. + + See + """ + assert n > 3 + + # find r and d such that temp_n-1 = 2^r × d + d = n - 1 + r = 0 + while d % 2 == 0: + d //= 2 + r += 1 + assert n - 1 == d * 2**r + assert d % 2 == 1 + + for _ in range(k): # each iteration divides risk of false prime by 4 + a = random.randint(2, n - 2) # choose a random witness + + x = pow(a, d, n) + if x == 1 or x == n - 1: + continue # go to next witness + + for _ in range(1, r): + x = x * x % n + if x == n - 1: + break # go to next witness + else: + return False + return True + + +def is_prime(n, mr_rounds=25): + """Test whether temp_n is probably prime + + See + + Arguments: + n (int): the number to be tested + mr_rounds (int, optional): number of Miller-Rabin iterations to run; + defaults to 25 iterations, which is what the GMP library uses + + Returns: + bool: when this function returns False, `temp_n` is composite (not prime); + when it returns True, `temp_n` is prime with overwhelming probability + """ + # as an optimization we quickly detect small primes using the list above + if n <= first_primes[-1]: + return n in first_primes + # for small dividors (relatively frequent), euclidean division is best + for p in first_primes: + if n % p == 0: + return False + # the actual generic test; give a false prime with probability 2⁻⁵⁰ + return miller_rabin(n, mr_rounds)