utils.py 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150
  1. from cryptography.hazmat.primitives import serialization, hashes
  2. from cryptography.hazmat.primitives.asymmetric import rsa, padding
  3. from cryptography.hazmat.backends import default_backend
  4. from cryptography.exceptions import InvalidSignature
  5. import random
  6. import pickle
  7. def extended_euclidean_algorithm(a, b):
  8. """
  9. Returns a three-tuple (gcd, x, y) such that
  10. a * x + b * y == gcd, where gcd is the greatest
  11. common divisor of a and b.
  12. This function implements the extended Euclidean
  13. algorithm and runs in O(log b) in the worst case.
  14. """
  15. s, old_s = 0, 1
  16. t, old_t = 1, 0
  17. r, old_r = b, a
  18. while r != 0:
  19. quotient = old_r // r
  20. old_r, r = r, old_r - quotient * r
  21. old_s, s = s, old_s - quotient * s
  22. old_t, t = t, old_t - quotient * t
  23. return old_r, old_s, old_t
  24. def inverse_of(n, p):
  25. """
  26. Returns the multiplicative inverse of
  27. n modulo p.
  28. This function returns an integer m such that
  29. (n * m) % p == 1.
  30. """
  31. gcd, x, y = extended_euclidean_algorithm(n, p)
  32. assert (n * x + p * y) % p == gcd
  33. if gcd != 1:
  34. # Either n is 0, or p is not a prime number.
  35. raise ValueError(
  36. '{} has no multiplicative inverse '
  37. 'modulo {}'.format(n, p))
  38. else:
  39. return x % p
  40. '''
  41. @param nums: list of weight
  42. @param true_rnd_fn: truely random function
  43. @return zero-based index of the truely selected element
  44. '''
  45. def weighted_random(nums, true_rnd_fn=random.random):
  46. """
  47. nums is list of weight, it return the truely random
  48. weighted value.
  49. """
  50. L = len(nums)
  51. pair = [(i, nums[i]) for i in range(L)]
  52. pair.sort(key=lambda p: p[1])
  53. tot = sum([pair[i][1] for i in range(L)])
  54. frequency = [pair[i][1]/tot for i in range(L)]
  55. acc_prop = [sum(frequency[:i+1]) for i in range(L)]
  56. rnd = true_rnd_fn()
  57. for elected in range(L):
  58. if rnd<=acc_prop[elected]:
  59. break
  60. return pair[elected][0]
  61. '''
  62. @param data: data is dictionary of list of (pk_i, s_i) public key,
  63. and stake respectively of the corresponding stakeholder U_i,
  64. seed of the leader election function.
  65. '''
  66. def encode_genesis_data(data):
  67. return pickle.dumps(data)
  68. def decode_gensis_data(encoded_data):
  69. return pickle.loads(encoded_data)
  70. '''
  71. TODO this is a adhoc solution
  72. this has is used to compute the state of block from the previous block
  73. '''
  74. def state_hash(obj):
  75. return hash(obj)
  76. '''
  77. TODO this is a adhoc solution
  78. this is used to generate VRF's sk from some seed
  79. note there is a need for nounce to be concatenated with the seed,
  80. just in case two stakeholders started with the same seed
  81. (for the time being the seed is provided by the stakeholder, it's stakeholder passowrd)
  82. '''
  83. def vrf_hash(seed):
  84. return hash(seed)
  85. def generate_sig_keys(private_key_password):
  86. ''' Generating the keys pair. Cryptographic algorithm used is for demostranation porpuses only. '''
  87. private_key = rsa.generate_private_key(
  88. public_exponent=65537,
  89. key_size=2048
  90. )
  91. encrypted_pem_private_key = private_key.private_bytes(
  92. encoding=serialization.Encoding.PEM,
  93. format=serialization.PrivateFormat.PKCS8,
  94. encryption_algorithm=serialization.BestAvailableEncryption(
  95. private_key_password.encode()))
  96. pem_public_key = private_key.public_key().public_bytes(
  97. encoding=serialization.Encoding.PEM,
  98. format=serialization.PublicFormat.SubjectPublicKeyInfo
  99. )
  100. return encrypted_pem_private_key, pem_public_key
  101. def sign_message(password, private_key, message):
  102. ''' Signs a message using private_key. '''
  103. privkey = serialization.load_pem_private_key(
  104. private_key, password=password.encode(), backend=default_backend())
  105. signed_message = privkey.sign(
  106. message.encode(),
  107. padding.PSS(
  108. mgf=padding.MGF1(hashes.SHA256()),
  109. salt_length=padding.PSS.MAX_LENGTH),
  110. hashes.SHA256()
  111. )
  112. return signed_message
  113. def verify_signature(public_key, message, signed_message):
  114. ''' Verifies a message against a public key. '''
  115. pubkey = serialization.load_pem_public_key(
  116. public_key, backend=default_backend())
  117. try:
  118. pubkey.verify(
  119. signed_message,
  120. message.encode(),
  121. padding.PSS(
  122. mgf=padding.MGF1(hashes.SHA256()),
  123. salt_length=padding.PSS.MAX_LENGTH),
  124. hashes.SHA256())
  125. return True
  126. except InvalidSignature:
  127. return False