In this program I am trying to send key shares though UDP broadcast to the client(s). The issue I'm facing is with encoding of strings and bytes into one message, there are number of errors are produced.
I would like to send from the server to the client a random ID and a key share, which include index (integer) and share (16-bytes string). I have added ":" between id, index and share to be able to manage it at the client later (by splitting the message into parts).
I have tried converting everything into string, such as:
message = (str(id) ":" str(index) ":").encode('utf-16') str(share, 'utf-16')
. But this causing issues in key reconstruction at the client, where the key share should be a byte string and it looks like b"b'\xff\xfej\xb9\xdb\x8c&\x7f\x06\x87\x98Ig\xfc\x1eJ\xf6\xb5'"
.
Then I have tried encoding id and index to utf-16 and sending a message to the client, and then decode it, but this does not let me reconstruct the key and I'm getting an error: ValueError: The encoded value must be an integer or a 16 byte string
.
When I decode at the client, the data looks like this: f91f7e52-865d-49bc-bb45-ad80255e9ef9:5:륪賛缦蜆䦘ﱧ䨞뗶
. However, some shares after decoding do not contain dilimiter and thus not being able to split.
Is there a way the server can send all the following data in one message (id, index, share), so that it can be separated correctly at the client?
The desired output at server would be (id = string, index = int, share = 16-byte string):
id = f91f7e52-865d-49bc-bb45-ad80255e9ef9
index = 5
share = b'\xff\xfej\xb9\xdb\x8c&\x7f\x06\x87\x98Ig\xfc\x1eJ\xf6\xb5'
Server.py
from socket import *
import socket
import time
import uuid
from Crypto.Random import get_random_bytes
from Crypto.Protocol.SecretSharing import Shamir
server = socket.socket(AF_INET, SOCK_DGRAM)
server.setsockopt(SOL_SOCKET, SO_REUSEPORT, 1)
server.setsockopt(SOL_SOCKET, SO_BROADCAST, 1)
id = uuid.uuid4()
# generate 16-byte key
key = get_random_bytes(16)
# prepare k chunks of key
shares = Shamir.split(3, 5, key)
while True:
for index, share in shares:
message = (str(id) ":" str(index) ":").encode('utf-16') share
# send bytes
server.sendto(message, ('<broadcast>', 37020))
print("message sent!")
time.sleep(1)
Client.py
from socket import *
from Crypto.Protocol.SecretSharing import Shamir
audienceSocket = socket(AF_INET, SOCK_DGRAM)
audienceSocket.setsockopt(SOL_SOCKET, SO_REUSEPORT, 1)
audienceSocket.bind(('', 37020))
receivedShares = {}
reconstruct = []
while True:
# get data from sender
data, address = audienceSocket.recvfrom(1024)
decoded = data.decode('utf-16')
print("Decoded:", decoded)
id, index = decoded.split(":", 1)
index, share = index.split(":", 1)
share = share.encode('utf-16')
print(id)
print(index)
print(share)
if id not in receivedShares:
receivedShares[id] = set()
receivedShares[id].add((index, share))
print(receivedShares)
for shares in receivedShares:
if len(shares) >= 3:
for share in shares:
reconstruct.append(share)
# reconstrcut the key
key = Shamir.combine(reconstruct)
print("The key is:", key)
CodePudding user response:
The UUID can be encoded as 16 bytes value, the integer e.g. as 4 bytes and and a share has a length of 16 bytes. Therefore the data can be simply concatenated without delimiter and separated by their lengths, e.g.:
import uuid
from Crypto.Random import get_random_bytes
from Crypto.Protocol.SecretSharing import Shamir
id_sender = uuid.uuid4()
key = get_random_bytes(16)
shares = Shamir.split(3, 5, key)
for index_sender, share_sender in shares:
# Encode
message = id_sender.bytes index_sender.to_bytes(4, byteorder='big') share_sender
# Send message via socket
# Decode
id_receiver = uuid.UUID(bytes=message[:16])
index_receiver = int.from_bytes(message[16:20], byteorder='big')
share_receiver = message[20:]
print(id_sender == id_receiver, index_sender == index_receiver, share_sender == share_receiver) # True True True