diff --git a/alex.keys b/alex.keys deleted file mode 100644 index 0d5f7d7..0000000 Binary files a/alex.keys and /dev/null differ diff --git a/block.dat b/block.dat deleted file mode 100644 index 74ee6ec..0000000 Binary files a/block.dat and /dev/null differ diff --git a/mike.keys b/mike.keys deleted file mode 100644 index 61de7bc..0000000 Binary files a/mike.keys and /dev/null differ diff --git a/period_2/01-sockets/801/client.py b/period_2/01-sockets/801/client.py new file mode 100644 index 0000000..2788c82 --- /dev/null +++ b/period_2/01-sockets/801/client.py @@ -0,0 +1,26 @@ +import socket + +HOST = socket.gethostbyname("localhost") +PORT = 5050 + +# create a message input +message = input("Please enter a message: ") + +# create and bind socket +socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) +socket.connect((HOST, PORT)) + + + +# send message +socket.sendall(message.encode()) + +# socket.recvmsg(1024) + +print("Message sent") +socket.close() + + + + + diff --git a/period_2/01-sockets/801/server.py b/period_2/01-sockets/801/server.py new file mode 100644 index 0000000..860d4e7 --- /dev/null +++ b/period_2/01-sockets/801/server.py @@ -0,0 +1,21 @@ +import socket + +HOST = socket.gethostbyname("localhost") +PORT = 8080 + +# create and bind socket +socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) +socket.bind((HOST, PORT)) + +# become a server socket +socket.listen(2) + +# accept connections +while True: + connection, address = socket.accept() + print("Connection from", address) + data = connection.recv(1024) + print("Received: ", data.decode()) + connection.close() + break + diff --git a/period_2/01-sockets/802/client.py b/period_2/01-sockets/802/client.py new file mode 100644 index 0000000..9e16085 --- /dev/null +++ b/period_2/01-sockets/802/client.py @@ -0,0 +1,28 @@ +import socket + +HOST = socket.gethostbyname("145.137.113.215") +PORT = 5055 + +# create a message input +message = "1001519" + +# create and bind socket +socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) +socket.connect((HOST, PORT)) + + + +# send message +socket.sendall(bytes(message, "utf-8")) +data = socket.recv(1024) + +# socket.recvmsg(1024) + +print("Message sent") + +socket.close() + + + + + diff --git a/period_2/01-sockets/803/client.py b/period_2/01-sockets/803/client.py new file mode 100644 index 0000000..ca50e3f --- /dev/null +++ b/period_2/01-sockets/803/client.py @@ -0,0 +1,31 @@ +import socket + +HOST = socket.gethostbyname("localhost") +PORT = 5052 + +def send(msg): + + + # create a message input + message = msg.encode("utf-8") + + # create and bind socket + socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + socket.connect((HOST, PORT)) + + # send message + socket.send(b'-' * (64 - len(str(len(message)).encode("utf-8")))) + socket.send(message) + + socket.recv(1024) + + print("Message sent") + socket.close() + +message = input("Please enter a message: ") + +if message == "": + send("!DISCONNECT") + +else: + send(message) \ No newline at end of file diff --git a/period_2/01-sockets/803/server.py b/period_2/01-sockets/803/server.py new file mode 100644 index 0000000..1de9242 --- /dev/null +++ b/period_2/01-sockets/803/server.py @@ -0,0 +1,34 @@ +import socket +# Get local machine name +HOST = socket.gethostname("localhost") +PORT = 5050 + +def handleClient(clientsocket): + while True: + # Receive no more than 1024 bytes + msg = clientsocket.recv(1024) + if not msg: + break + clientsocket.send(msg) + clientsocket.close() + +def start(): + # Create a socket object + serversocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + + + # Bind to the port + serversocket.bind((HOST, PORT)) + + # Queue up to 5 requests + serversocket.listen(5) + + while True: + # Establish a connection + clientsocket, addr = serversocket.accept() + + print("Got a connection from %s" % str(addr)) + + msg = 'Thank you for connecting' + "\r\n" + clientsocket.send(msg.encode('ascii')) + clientsocket.close() \ No newline at end of file diff --git a/period_2/01-sockets/804/client.py b/period_2/01-sockets/804/client.py new file mode 100644 index 0000000..8ed6636 --- /dev/null +++ b/period_2/01-sockets/804/client.py @@ -0,0 +1,30 @@ +import socket + +HOST = socket.gethostbyname("localhost") +PORT = 5053 +ADDR = (HOST, PORT) +HEADER = 64 +FORMAT = "utf-8" + +server = socket.socket(socket.AF_INET, socket.SOCK_STREAM) +server.connect(ADDR) + +def send(message): + message = message.encode(FORMAT) + msg_length = len(message) + send_length = str(msg_length).encode(FORMAT) + send_length += b' ' * (HEADER - len(send_length)) + server.send(send_length) + server.send(message) + print(server.recv(2048).decode(FORMAT)) + +def handleSend(): + connected = True + while connected: + message = input("Please enter a message: ") + if message == "!DISCONNECT": + connected = False + else: + send(message) + +handleSend() \ No newline at end of file diff --git a/period_2/01-sockets/804/server.py b/period_2/01-sockets/804/server.py new file mode 100644 index 0000000..c7c5caf --- /dev/null +++ b/period_2/01-sockets/804/server.py @@ -0,0 +1,44 @@ +import socket +import threading + +HOST = socket.gethostbyname("localhost") +PORT = 5053 +ADDR = (HOST, PORT) +HEADER = 64 +FORMAT = "utf-8" + +server = socket.socket(socket.AF_INET, socket.SOCK_STREAM) +server.bind(ADDR) + +def handleClient(conn, addr): + client_name = conn.recv(HEADER).decode(FORMAT) + print(f"[NEW CONNECTION] {client_name} connected.") + connected = True + while connected: + msg_length = conn.recv(HEADER).decode(FORMAT) + if msg_length: + msg_length = int(len(msg_length)) + msg = conn.recv(msg_length).decode(FORMAT) + if msg == "!DISCONNECT": + connected = False + print(f"[{client_name}] {msg}") + conn.send("Msg received".encode(FORMAT)) + + conn.send("Bye bye".encode(FORMAT)) + conn.close() + print(f"[DISCONNECTED] {client_name} disconnected.") + print(f"[ACTIVE CONNECTIONS] {threading.activeCount() - 2}]") + + + +def start(): + server.listen() + print(f"[LISTENING] Server is listening on {HOST}") + while True: + conn, addr = server.accept() + thread = threading.Thread(target=handleClient, args=(conn, addr)) + thread.start() + print(f"[ACTIVE CONNECTIONS] {threading.activeCount() - 1}") + +print("[STARTING] server is starting...") +start() \ No newline at end of file diff --git a/period_2/01-sockets/807_T07_A07_SendReceiveBlock_Local/BlockChain.py b/period_2/01-sockets/807_T07_A07_SendReceiveBlock_Local/BlockChain.py new file mode 100644 index 0000000..c20e155 --- /dev/null +++ b/period_2/01-sockets/807_T07_A07_SendReceiveBlock_Local/BlockChain.py @@ -0,0 +1,25 @@ +from cryptography.hazmat.backends import default_backend +from cryptography.hazmat.primitives import hashes + +class CBlock: + + data = None + previousHash = None + previousBlock = None + def __init__(self, data, previousBlock): + self.data = data + self.blockHash = None + self.previousBlock = previousBlock + if previousBlock != None: + self.previousHash = previousBlock.computeHash() + + def computeHash(self): + digest = hashes.Hash(hashes.SHA256(), backend=default_backend()) + digest.update(bytes(str(self.data),'utf8')) + digest.update(bytes(str(self.previousHash),'utf8')) + return digest.finalize() + + def is_valid(self): + if self.previousBlock == None: + return True + return self.previousBlock.computeHash() == self.previousHash \ No newline at end of file diff --git a/period_2/01-sockets/807_T07_A07_SendReceiveBlock_Local/Signature.py b/period_2/01-sockets/807_T07_A07_SendReceiveBlock_Local/Signature.py new file mode 100644 index 0000000..740ee1a --- /dev/null +++ b/period_2/01-sockets/807_T07_A07_SendReceiveBlock_Local/Signature.py @@ -0,0 +1,41 @@ +from cryptography.exceptions import * +from cryptography.hazmat.primitives.asymmetric import rsa +from cryptography.hazmat.primitives import hashes +from cryptography.hazmat.primitives.asymmetric import padding +from cryptography.hazmat.primitives import serialization + +def generate_keys(): + private_key = rsa.generate_private_key(public_exponent=65537,key_size=2048) + public_key = private_key.public_key() + + pbc_ser = public_key.public_bytes( + encoding=serialization.Encoding.PEM, + format=serialization.PublicFormat.SubjectPublicKeyInfo) + return private_key, pbc_ser + +def sign(message, private_key): + message = bytes(str(message), 'utf-8') + signature = private_key.sign( + message, + padding.PSS(mgf=padding.MGF1(hashes.SHA256()), salt_length=padding.PSS.MAX_LENGTH), + hashes.SHA256() + ) + return signature + +def verify(message, signature, pbc_ser): + message = bytes(str(message), 'utf-8') + public_key = serialization.load_pem_public_key(pbc_ser) + try: + public_key.verify( + signature, + message, + padding.PSS(mgf=padding.MGF1(hashes.SHA256()), + salt_length=padding.PSS.MAX_LENGTH), + hashes.SHA256() + ) + return True + except InvalidSignature: + return False + except: + print("Error executing 'public_key.verify'") + return False \ No newline at end of file diff --git a/period_2/01-sockets/807_T07_A07_SendReceiveBlock_Local/Transaction.py b/period_2/01-sockets/807_T07_A07_SendReceiveBlock_Local/Transaction.py new file mode 100644 index 0000000..518ac8f --- /dev/null +++ b/period_2/01-sockets/807_T07_A07_SendReceiveBlock_Local/Transaction.py @@ -0,0 +1,100 @@ +from gzip import READ +from operator import truediv +from optparse import AmbiguousOptionError + +REWARD_VALUE = 25.0 +NORMAL = 0 +REWARD = 1 + +from Signature import * + +class Tx: + def __init__(self, type = NORMAL): + + self.type = type + self.inputs = [] + self.outputs = [] + self.sigs = [] + self.reqd = [] + + def add_input(self, from_addr, amount): + self.inputs.append((from_addr, amount)) + + def add_output(self, to_addr, amount): + self.outputs.append((to_addr, amount)) + + def add_reqd(self, addr): + self.reqd.append(addr) + + def sign(self, private): + message = self.__gather() + newsig = sign(message, private) + self.sigs.append(newsig) + + def is_valid(self): + + if self.type == REWARD: + if len(self.inputs)!=0 and len(self.outputs)!=1: + return False + return True + + else: + total_in = 0 + total_out = 0 + message = self.__gather() + for addr,amount in self.inputs: + found = False + for s in self.sigs: + if verify(message, s, addr): + found = True + if not found: + # print ("No good sig found for " + str(message)) + return False + if amount < 0: + return False + total_in = total_in + amount + for addr in self.reqd: + found = False + for s in self.sigs: + if verify(message, s, addr): + found = True + if not found: + return False + for addr,amount in self.outputs: + if amount < 0: + return False + total_out = total_out + amount + + if total_out > total_in: + # print("Outputs exceed inputs") + return False + return True + + def __gather(self): + data=[] + data.append(self.inputs) + data.append(self.outputs) + data.append(self.reqd) + return data + + def __repr__(self): + + repr_str = "INPUTS:\n" + for addr, amt in self.inputs: + repr_str = repr_str + str(amt) + "from" + str(addr) + "\n" + + repr_str += "OUTPUTS:\n" + for addr, amt in self.outputs: + repr_str = repr_str + str(amt) + "to" + str(addr) + "\n" + + repr_str += "EXTRA REQUIRED SIGNATURES:\n" + for req_sig in self.reqd: + repr_str = repr_str + str(req_sig) + "\n" + + repr_str += "SIGNATURES:\n" + for sig in self.sigs: + repr_str = repr_str + str(sig) + "\n" + + repr_str += "END\n" + + return repr_str diff --git a/period_2/01-sockets/807_T07_A07_SendReceiveBlock_Local/TxBlock.py b/period_2/01-sockets/807_T07_A07_SendReceiveBlock_Local/TxBlock.py new file mode 100644 index 0000000..4bb47ed --- /dev/null +++ b/period_2/01-sockets/807_T07_A07_SendReceiveBlock_Local/TxBlock.py @@ -0,0 +1,63 @@ +from BlockChain import CBlock +from Signature import generate_keys, sign, verify +from cryptography.hazmat.primitives import hashes +from cryptography.hazmat.backends import default_backend + +import random + +REWARD_VALUE = 25.0 +leading_zeros = 2 +next_char_limit = 20 + +class TxBlock (CBlock): + + def __init__(self, previousBlock): + self.nonce = "A random nonce" + super(TxBlock, self).__init__([], previousBlock) + + def addTx(self, Tx_in): + self.data.append(Tx_in) + + def __count_totals(self): + total_in = 0 + total_out = 0 + for tx in self.data: + for addr, amt in tx.inputs: + total_in = total_in + amt + for addr, amt in tx.outputs: + total_out = total_out + amt + return total_in, total_out + + def is_valid(self): + if not super(TxBlock, self).is_valid(): + return False + for tx in self.data: + if not tx.is_valid(): + return False + + if self.nonce != "A random nonce": + if not self.good_nonce(): + return False + + total_in, total_out = self.__count_totals() + + Tx_Balance = round(total_out - total_in, 10) + + if Tx_Balance > REWARD_VALUE: + return False + return True + + def good_nonce(self): + digest = hashes.Hash(hashes.SHA256(), backend=default_backend()) + digest.update(bytes(str(self.data), 'utf8')) + digest.update(bytes(str(self.previousHash), 'utf8')) + digest.update(bytes(str(self.nonce), 'utf8')) + this_hash = digest.finalize() + return this_hash[:leading_zeros] == b'\x00'*leading_zeros + + def find_nonce(self): + for i in range(10000000): + self.nonce = i + if self.good_nonce(): + return self.nonce + return None diff --git a/period_2/01-sockets/807_T07_A07_SendReceiveBlock_Local/client.py b/period_2/01-sockets/807_T07_A07_SendReceiveBlock_Local/client.py new file mode 100644 index 0000000..5f3b118 --- /dev/null +++ b/period_2/01-sockets/807_T07_A07_SendReceiveBlock_Local/client.py @@ -0,0 +1,11 @@ +import pickle +import socket + +PORT = 5005 + +def sendObj(ip_address, obj): + sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + sock.connect((ip_address, PORT)) + sock.send(pickle.dumps(obj)) + sock.close() + return False \ No newline at end of file diff --git a/period_2/01-sockets/807_T07_A07_SendReceiveBlock_Local/client_t.py b/period_2/01-sockets/807_T07_A07_SendReceiveBlock_Local/client_t.py new file mode 100644 index 0000000..76fad33 --- /dev/null +++ b/period_2/01-sockets/807_T07_A07_SendReceiveBlock_Local/client_t.py @@ -0,0 +1,36 @@ +from TxBlock import * +from Transaction import * +from Signature import * + +from client import * + +SERVER = 'localhost' +TCP_PORT = 5050 + +if __name__ == "__main__": + + alex_prv, alex_pbc = generate_keys() + mike_prv, mike_pbc = generate_keys() + rose_prv, rose_pbc = generate_keys() + mara_prv, mara_pbc = generate_keys() + + Tx1 = Tx() + Tx1.add_input(alex_pbc, 2.3) + Tx1.add_output(mike_pbc, 1.0) + Tx1.add_output(rose_pbc, 1.1) + Tx1.sign(alex_prv) + + Tx2 = Tx() + Tx2.add_input(rose_pbc, 2.3) + Tx2.add_input(mike_pbc, 1.0) + Tx2.add_output(alex_pbc, 3.1) + Tx2.sign(mike_prv) + Tx2.sign(rose_prv) + + B1 = TxBlock(None) + B1.addTx(Tx1) + B1.addTx(Tx2) + + sendObj(SERVER, B1) + + sendObj(SERVER, Tx2) diff --git a/period_2/01-sockets/807_T07_A07_SendReceiveBlock_Local/server.py b/period_2/01-sockets/807_T07_A07_SendReceiveBlock_Local/server.py new file mode 100644 index 0000000..1e9cdf1 --- /dev/null +++ b/period_2/01-sockets/807_T07_A07_SendReceiveBlock_Local/server.py @@ -0,0 +1,21 @@ +import socket +import pickle +import TxBlock + +PORT = 5050 + +def newConnection(ip_address): + sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + sock.bind((ip_address, PORT)) + sock.listen(2) + return sock + +def recvObj(socket): + new_sock, address = socket.accept() + data = new_sock.recv(1024) + print("Received: ", data) + data = pickle.loads(data) + new_sock.close() + return data + + diff --git a/period_2/01-sockets/807_T07_A07_SendReceiveBlock_Local/server_t.py b/period_2/01-sockets/807_T07_A07_SendReceiveBlock_Local/server_t.py new file mode 100644 index 0000000..c3d077b --- /dev/null +++ b/period_2/01-sockets/807_T07_A07_SendReceiveBlock_Local/server_t.py @@ -0,0 +1,50 @@ +import TxBlock +import socket +import pickle + +from server import * + +SERVER = 'localhost' +TCP_PORT = 5050 +# BUFFER_SIZE = 1024 + +if __name__ == "__main__": + + server = newConnection(SERVER) + + newB = recvObj(server) + # print(newB.data[0]) + # print(newB.data[1]) + + if (newB.is_valid()): + print("Success! Transaction is valid.") + else: + print("Error! Transaction invalid.") + + if newB.data[0].inputs[0][1] == 2.3: + print("Success! Input value matches.") + else: + print("Error! Wrong input value for block 1, transaction 1.") + + if newB.data[0].outputs[1][1] == 1.1: + print("Success! Output value matches.") + else: + print("Error! Wrong output value for block 1, transaction 1.") + + if newB.data[1].inputs[0][1] == 2.3: + print("Success! Input value matches.") + else: + print("Error! Wrong input value for block 1, transaction 1.") + + if newB.data[1].inputs[1][1] == 1.0: + print("Success! Input value matches.") + else: + print("Error! Wrong input value for block 1, transaction 1.") + + if newB.data[1].outputs[0][1] == 3.1: + print("Success! Output value matches.") + else: + print("Error! Wrong output value for block 1, transaction 1.") + + newTx = recvObj(server) + # print(newTx) diff --git a/period_2/01-sockets/808_T08_A08_SendReceiveBlock_P2P/client_t.py b/period_2/01-sockets/808_T08_A08_SendReceiveBlock_P2P/client_t.py new file mode 100644 index 0000000..c13fc49 --- /dev/null +++ b/period_2/01-sockets/808_T08_A08_SendReceiveBlock_P2P/client_t.py @@ -0,0 +1,36 @@ +from TxBlock import * +from Transaction import * +from Signature import * + +from client import * + +SERVER = 'localhost' +TCP_PORT = 5005 + +if __name__ == "__main__": + + alex_prv, alex_pbc = generate_keys() + mike_prv, mike_pbc = generate_keys() + rose_prv, rose_pbc = generate_keys() + mara_prv, mara_pbc = generate_keys() + + Tx1 = Tx() + Tx1.add_input(alex_pbc, 2.3) + Tx1.add_output(mike_pbc, 1.0) + Tx1.add_output(rose_pbc, 1.1) + Tx1.sign(alex_prv) + + Tx2 = Tx() + Tx2.add_input(rose_pbc, 2.3) + Tx2.add_input(mike_pbc, 1.0) + Tx2.add_output(alex_pbc, 3.1) + Tx2.sign(mike_prv) + Tx2.sign(rose_prv) + + B1 = TxBlock.TxBlock(None) + B1.addTx(Tx1) + B1.addTx(Tx2) + + sendObj(SERVER, B1) + + sendObj(SERVER, Tx2) diff --git a/period_2/01-sockets/808_T08_A08_SendReceiveBlock_P2P/server_t.py b/period_2/01-sockets/808_T08_A08_SendReceiveBlock_P2P/server_t.py new file mode 100644 index 0000000..12ad431 --- /dev/null +++ b/period_2/01-sockets/808_T08_A08_SendReceiveBlock_P2P/server_t.py @@ -0,0 +1,57 @@ +import TxBlock +import socket +import pickle + +from server import * + + +local_hostname = socket.gethostname() +# local_ip = socket.gethostbyname(local_hostname) +local_ip = '192.168.1.103' + + +TCP_PORT = 5005 +BUFFER_SIZE = 1024 + +if __name__ == "__main__": + + server = newConnection(local_ip) + print(f"[LISTENING] Server is listening on {local_ip}") + + + newB = recvObj(server) + # print(newB.data[0]) + # print(newB.data[1]) + + if (newB.is_valid()): + print("Success! Transaction is valid.") + else: + print("Error! Transaction invalid.") + + if newB.data[0].inputs[0][1] == 2.3: + print("Success! Input value matches.") + else: + print("Error! Wrong input value for block 1, transaction 1.") + + if newB.data[0].outputs[1][1] == 1.1: + print("Success! Output value matches.") + else: + print("Error! Wrong output value for block 1, transaction 1.") + + if newB.data[1].inputs[0][1] == 2.3: + print("Success! Input value matches.") + else: + print("Error! Wrong input value for block 1, transaction 1.") + + if newB.data[1].inputs[1][1] == 1.0: + print("Success! Input value matches.") + else: + print("Error! Wrong input value for block 1, transaction 1.") + + if newB.data[1].outputs[0][1] == 3.1: + print("Success! Output value matches.") + else: + print("Error! Wrong output value for block 1, transaction 1.") + + newTx = recvObj(server) + # print(newTx) diff --git a/period_2/02-sockets/901_T01_A01_CS_MultiCon_Sync_NonB_Recv.zip/client.py b/period_2/02-sockets/901_T01_A01_CS_MultiCon_Sync_NonB_Recv.zip/client.py new file mode 100644 index 0000000..ff3e7dd --- /dev/null +++ b/period_2/02-sockets/901_T01_A01_CS_MultiCon_Sync_NonB_Recv.zip/client.py @@ -0,0 +1,52 @@ +import socket +from threading import Timer + +HEADER = 64 +PORT = 5053 +FORMAT = 'utf-8' +DISCONNECT_MESSAGE = "!DISCONNECT" +HOST_IP = 'localhost' + +ADDR = (HOST_IP, PORT) + +client = socket.socket(socket.AF_INET, socket.SOCK_STREAM) +client.connect(ADDR) +client_name = input('please enter your name: ') +client.send(client_name.encode(FORMAT)) +print(client.recv(2048).decode(FORMAT)) + +def send(msg): + message = msg.encode(FORMAT) + msg_length = len(message) + send_length = str(msg_length).encode(FORMAT) + send_length += b' ' * (HEADER - len(send_length)) + client.send(send_length) + client.send(message) + print(client.recv(2048).decode(FORMAT)) + +def stop_the_client(): + global cont_flag + mes = DISCONNECT_MESSAGE + send(mes) + cont_flag = False + +cont_flag = True +while cont_flag: + timeout = 20 + timeout_thread = Timer(timeout, send, args=(DISCONNECT_MESSAGE,)) + timeout_thread.start() + + if not cont_flag: + print("the connection is closed by the server") + break + print('--------------------------') + print('You can send a message to server') + print('to stop connection, press enter on a blank message') + mes = input('Your message: ') + if mes: + send(mes) + else: + send(DISCONNECT_MESSAGE) + cont_flag = False + + timeout_thread.cancel() diff --git a/period_2/02-sockets/901_T01_A01_CS_MultiCon_Sync_NonB_Recv.zip/server.py b/period_2/02-sockets/901_T01_A01_CS_MultiCon_Sync_NonB_Recv.zip/server.py new file mode 100644 index 0000000..b199056 --- /dev/null +++ b/period_2/02-sockets/901_T01_A01_CS_MultiCon_Sync_NonB_Recv.zip/server.py @@ -0,0 +1,65 @@ +import socket +import threading +import select + +HEADER = 64 +PORT = 5053 +local_ip = socket.gethostbyname('localhost') +ADDR = (local_ip, PORT) +FORMAT = 'utf-8' +DISCONNECT_MESSAGE = "!DISCONNECT" + +server = socket.socket(socket.AF_INET, socket.SOCK_STREAM) +server.bind(ADDR) + +def handle_client(conn, addr): + client_name = conn.recv(2048).decode(FORMAT) + + print(f"\n[NEW CONNECTION] {client_name}@{addr} is connected.") + connection_message = f"...\nHi {client_name}! \nYou are successfully connected to the server {ADDR}" + conn.send(connection_message.encode(FORMAT)) + connected = True + while connected: + ready_to_read, ready_to_write, in_error = select.select([conn], [], [conn], 20) + print(f"ready_to_read: {ready_to_read}") + if len(ready_to_read): + print(f"ready_to_read: {ready_to_read}") + msg_length = conn.recv(HEADER).decode(FORMAT) + print(f"msg_length: {msg_length}") + if msg_length: + + msg_length = int(msg_length) + msg = conn.recv(msg_length).decode(FORMAT) + if msg == DISCONNECT_MESSAGE: + connected = False + + print(f"[{client_name}@{addr}]>> {msg}") + return_message = f'Server received your message: "{msg}"' + conn.send(return_message.encode(FORMAT)) + else: + connected = False + return_message = f"\nTimeout! {client_name} is disconnected from the server {ADDR}" + conn.send(return_message.encode(FORMAT)) + + bye_message = f"\nBye {client_name}!" + conn.send(bye_message.encode(FORMAT)) + conn.close() + print(f"[ACTIVE CONNECTIONS] {threading.active_count() - 2}") + + # bye_message = f"\nBye {client_name}!" + # conn.send(bye_message.encode(FORMAT)) + conn.close() + print(f"[ACTIVE CONNECTIONS] {threading.active_count() - 2}") + +def start(): + server.listen() + print(f"[LISTENING] Server is listening on {local_ip}") + while True: + conn, addr = server.accept() + thread = threading.Thread(target=handle_client, args=(conn, addr)) + thread.start() + print(f"[ACTIVE CONNECTIONS] {threading.active_count() - 1}") + + +print("[STARTING] server is starting...") +start() \ No newline at end of file diff --git a/period_2/02-sockets/902/BlockChain.py b/period_2/02-sockets/902/BlockChain.py new file mode 100644 index 0000000..d3879c9 --- /dev/null +++ b/period_2/02-sockets/902/BlockChain.py @@ -0,0 +1,24 @@ +from cryptography.hazmat.backends import default_backend +from cryptography.hazmat.primitives import hashes + +class CBlock: + + data = None + previousHash = None + previousBlock = None + def __init__(self, data, previousBlock): + self.data = data + self.previousBlock = previousBlock + if previousBlock != None: + self.previousHash = previousBlock.computeHash() + + def computeHash(self): + digest = hashes.Hash(hashes.SHA256(), backend=default_backend()) + digest.update(bytes(str(self.data),'utf8')) + digest.update(bytes(str(self.previousHash),'utf8')) + return digest.finalize() + + def is_valid(self): + if self.previousBlock == None: + return True + return self.previousBlock.computeHash() == self.previousHash \ No newline at end of file diff --git a/period_2/02-sockets/902/Client_test.py b/period_2/02-sockets/902/Client_test.py new file mode 100644 index 0000000..1f0c60d --- /dev/null +++ b/period_2/02-sockets/902/Client_test.py @@ -0,0 +1,65 @@ +from TxBlock import * +from Transaction import * +from Signature import * + +from SocketUtil import * + +SERVER = 'localhost' +TCP_PORT = 5050 + +if __name__ == "__main__": + + alex_prv, alex_pbc = generate_keys() + mike_prv, mike_pbc = generate_keys() + rose_prv, rose_pbc = generate_keys() + mara_prv, mara_pbc = generate_keys() + + Tx1 = Tx() + Tx1.add_input(alex_pbc, 2.3) + Tx1.add_output(mike_pbc, 1.0) + Tx1.add_output(rose_pbc, 1.1) + Tx1.sign(alex_prv) + + Tx2 = Tx() + Tx2.add_input(rose_pbc, 2.3) + Tx2.add_input(mike_pbc, 1.0) + Tx2.add_output(alex_pbc, 3.1) + Tx2.sign(mike_prv) + Tx2.sign(rose_prv) + + B1 = TxBlock(None) + B1.addTx(Tx1) + B1.addTx(Tx2) + + server = newServerSocket(SERVER) + + print('A connection to the server is established.') + + # ------------------------------------- + sendObj(SERVER, Tx2) + print('Server is in Receiving mode ...') + Obj = recvObj(server) + if Obj: + print('Data: ', type(Obj),'\n') + else: + print('No object received') + + # ------------------------------------- + sendObj(SERVER, B1) + print('Server is in Receiving mode ...') + Obj = recvObj(server) + if Obj: + print('Data: ', type(Obj),'\n') + else: + print('No object received') + + # ------------------------------------- + print('Server is in Receiving mode ...') + Obj = recvObj(server) + if Obj: + print(Obj) + else: + print('No object received') + + print("Success! The connection is released.") # If returns after time, then successful + server.close() diff --git a/period_2/02-sockets/902/Signature.py b/period_2/02-sockets/902/Signature.py new file mode 100644 index 0000000..740ee1a --- /dev/null +++ b/period_2/02-sockets/902/Signature.py @@ -0,0 +1,41 @@ +from cryptography.exceptions import * +from cryptography.hazmat.primitives.asymmetric import rsa +from cryptography.hazmat.primitives import hashes +from cryptography.hazmat.primitives.asymmetric import padding +from cryptography.hazmat.primitives import serialization + +def generate_keys(): + private_key = rsa.generate_private_key(public_exponent=65537,key_size=2048) + public_key = private_key.public_key() + + pbc_ser = public_key.public_bytes( + encoding=serialization.Encoding.PEM, + format=serialization.PublicFormat.SubjectPublicKeyInfo) + return private_key, pbc_ser + +def sign(message, private_key): + message = bytes(str(message), 'utf-8') + signature = private_key.sign( + message, + padding.PSS(mgf=padding.MGF1(hashes.SHA256()), salt_length=padding.PSS.MAX_LENGTH), + hashes.SHA256() + ) + return signature + +def verify(message, signature, pbc_ser): + message = bytes(str(message), 'utf-8') + public_key = serialization.load_pem_public_key(pbc_ser) + try: + public_key.verify( + signature, + message, + padding.PSS(mgf=padding.MGF1(hashes.SHA256()), + salt_length=padding.PSS.MAX_LENGTH), + hashes.SHA256() + ) + return True + except InvalidSignature: + return False + except: + print("Error executing 'public_key.verify'") + return False \ No newline at end of file diff --git a/period_2/02-sockets/902/SocketUtil.py b/period_2/02-sockets/902/SocketUtil.py new file mode 100644 index 0000000..320bd26 --- /dev/null +++ b/period_2/02-sockets/902/SocketUtil.py @@ -0,0 +1,38 @@ +import socket +import pickle +import select + +TCP_PORT = 5050 +BUFFER_SIZE = 1024 + + +def newServerSocket(ip_addr): + server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + server_socket.bind((ip_addr, TCP_PORT)) + server_socket.listen() + return server_socket + + +def recvObj(socket): + ready_to_read, ready_to_write, in_error = select.select([socket], [], [socket], 20) + if socket in ready_to_read: + print('Server is ready to receive data ...') + connected_socket, addr = socket.accept() + print('Server is receiving data ...') + all_data = b'' + while True: + data = connected_socket.recv(BUFFER_SIZE) + if not data: + break + all_data = all_data + data + return pickle.loads(all_data) + return None + + +def sendObj(ip_addr, obj): + soc = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + soc.connect((ip_addr, TCP_PORT)) + data = pickle.dumps(obj) + soc.send(data) + soc.close() + return False diff --git a/period_2/02-sockets/902/SocketUtil_t.py b/period_2/02-sockets/902/SocketUtil_t.py new file mode 100644 index 0000000..b922221 --- /dev/null +++ b/period_2/02-sockets/902/SocketUtil_t.py @@ -0,0 +1,18 @@ +from TxBlock import * +from Transaction import * +from Signature import * + +from SocketUtil import * + +TCP_PORT = 5050 +BUFFER_SIZE = 1024 +SERVER = 'localhost' + +if __name__ == "__main__": + + server = newServerSocket(SERVER) + print('A connection to the server is established.') + Obj = recvObj(server) + + print("Success! The connection is released.") # If returns after time, then successful + server.close() \ No newline at end of file diff --git a/period_2/02-sockets/902/Transaction.py b/period_2/02-sockets/902/Transaction.py new file mode 100644 index 0000000..7f2fe0e --- /dev/null +++ b/period_2/02-sockets/902/Transaction.py @@ -0,0 +1,88 @@ +from Signature import * + +class Tx: + inputs = None + outputs =None + sigs = None + reqd = None + def __init__(self): + self.inputs = [] + self.outputs = [] + self.sigs = [] + self.reqd = [] + + def add_input(self, from_addr, amount): + self.inputs.append((from_addr, amount)) + + def add_output(self, to_addr, amount): + self.outputs.append((to_addr, amount)) + + def add_reqd(self, addr): + self.reqd.append(addr) + + def sign(self, private): + message = self.__gather() + newsig = sign(message, private) + self.sigs.append(newsig) + + def is_valid(self): + total_in = 0 + total_out = 0 + message = self.__gather() + for addr,amount in self.inputs: + found = False + for s in self.sigs: + if verify(message, s, addr): + found = True + if not found: + # print ("No good sig found for " + str(message)) + return False + if amount < 0: + return False + total_in = total_in + amount + for addr in self.reqd: + found = False + for s in self.sigs: + if verify(message, s, addr): + found = True + if not found: + return False + for addr,amount in self.outputs: + if amount < 0: + return False + total_out = total_out + amount + + # if total_out > total_in: + # # print("Outputs exceed inputs") + # return False + + return True + + def __gather(self): + data=[] + data.append(self.inputs) + data.append(self.outputs) + data.append(self.reqd) + return data + + def __repr__(self): + + repr_str = "INPUTS:\n" + for addr, amt in self.inputs: + repr_str = repr_str + str(amt) + "from" + str(addr) + "\n" + + repr_str += "OUTPUTS:\n" + for addr, amt in self.outputs: + repr_str = repr_str + str(amt) + "to" + str(addr) + "\n" + + repr_str += "EXTRA REQUIRED SIGNATURES:\n" + for req_sig in self.reqd: + repr_str = repr_str + str(req_sig) + "\n" + + repr_str += "SIGNATURES:\n" + for sig in self.sigs: + repr_str = repr_str + str(sig) + "\n" + + repr_str += "END\n" + + return repr_str diff --git a/period_2/02-sockets/902/TxBlock.py b/period_2/02-sockets/902/TxBlock.py new file mode 100644 index 0000000..879fece --- /dev/null +++ b/period_2/02-sockets/902/TxBlock.py @@ -0,0 +1,60 @@ +from BlockChain import CBlock +from Signature import generate_keys, sign, verify +from cryptography.hazmat.primitives import hashes +from cryptography.hazmat.backends import default_backend + +import random + +reward = 25.0 +leading_zeros = 2 +next_char_limit = 20 + +class TxBlock(CBlock): + + nonce = "A random nonce" + + def __init__(self, previousBlock): + super(TxBlock, self).__init__([], previousBlock) + + def addTx(self, Tx_in): + self.data.append(Tx_in) + + def __count_totals(self): + total_in = 0 + total_out = 0 + for tx in self.data: + for addr, amt in tx.inputs: + total_in = total_in + amt + for addr, amt in tx.outputs: + total_out = total_out + amt + return total_in, total_out + + def is_valid(self): + if not super(TxBlock, self).is_valid(): + return False + for tx in self.data: + if not tx.is_valid(): + return False + total_in, total_out = self.__count_totals() + if total_out - total_in - reward > 0.000000000001: + return False + return True + + def good_nonce(self): + digest = hashes.Hash(hashes.SHA256(), backend=default_backend()) + digest.update(bytes(str(self.data), 'utf8')) + digest.update(bytes(str(self.previousHash), 'utf8')) + digest.update(bytes(str(self.nonce), 'utf8')) + this_hash = digest.finalize() + + if this_hash[:leading_zeros] != bytes(''.join(['\x4f' for i in range(leading_zeros)]), 'utf8'): + return False + return int(this_hash[leading_zeros]) < next_char_limit + + def find_nonce(self): + for i in range(1000000): + self.nonce = ''.join([ + chr(random.randint(0, 255)) for i in range(10*leading_zeros)]) + if self.good_nonce(): + return self.nonce + return None diff --git a/period_2/02-sockets/903/BlockChain.py b/period_2/02-sockets/903/BlockChain.py new file mode 100644 index 0000000..d3879c9 --- /dev/null +++ b/period_2/02-sockets/903/BlockChain.py @@ -0,0 +1,24 @@ +from cryptography.hazmat.backends import default_backend +from cryptography.hazmat.primitives import hashes + +class CBlock: + + data = None + previousHash = None + previousBlock = None + def __init__(self, data, previousBlock): + self.data = data + self.previousBlock = previousBlock + if previousBlock != None: + self.previousHash = previousBlock.computeHash() + + def computeHash(self): + digest = hashes.Hash(hashes.SHA256(), backend=default_backend()) + digest.update(bytes(str(self.data),'utf8')) + digest.update(bytes(str(self.previousHash),'utf8')) + return digest.finalize() + + def is_valid(self): + if self.previousBlock == None: + return True + return self.previousBlock.computeHash() == self.previousHash \ No newline at end of file diff --git a/period_2/02-sockets/903/Miner_a.py b/period_2/02-sockets/903/Miner_a.py new file mode 100644 index 0000000..e36c03f --- /dev/null +++ b/period_2/02-sockets/903/Miner_a.py @@ -0,0 +1,86 @@ + +from SocketUtil import * +import TxBlock as Tx +# import Tx + +SERVER = 'localhost' + +wallet_list = [SERVER] +tx_list = [] + +def minerServer(my_ip, wallet_list): + miner_server_socket = newServerSocket(my_ip, 5050) + + # receive 2 transactions + for i in range(10): + newTx = recvObj(miner_server_socket) + if isinstance(newTx, Tx): + tx_list.append(newTx) + print("Received Tx") + if len(tx_list) == 2: + break + + if len(tx_list) < 2: + print("Error! Not enough transactions received.") + return + + # collect into block + newBlock = TxBlock(None) + for tx in tx_list: + newBlock.addTx(tx) + + # find nonce + newBlock.find_nonce() + if newBlock.good_nonce(): + print("Success! Nonce is valid.") + else: + print("Error! Nonce is invalid.") + return + + # send that block to each in wallet_list + for wallet in wallet_list: + sendObj(wallet, newBlock) + + return + + + # Open Server Connection + # Rec'v 2 transactions + # Collect into block + # Find nonce + # Send that block to each in walle_list + # return + + +def minerServer(my_ip, wallet_list): + # open server connection for the miner + server_socket = newServerSocket(my_ip, 5050) + print('Server is ready to receive data ...') + + # receive 2 transactions + data = recvObj(server_socket) + print('Server is receiving data ...') + if data: + print('Data: ', type(data), '\n') + else: + print('No object received') + + # collect into block + if data: + block = TxBlock(None) + block.addTx(data) + data = recvObj(server_socket) + if data: + block.addTx(data) + server_socket.close() + + # find nonce + block.find_nonce() + # send that block to each in wallet_list + for wallet in wallet_list: + sendObj(wallet, block) + + return + + +minerServer('localhost', wallet_list) diff --git a/period_2/02-sockets/903/Signature.py b/period_2/02-sockets/903/Signature.py new file mode 100644 index 0000000..740ee1a --- /dev/null +++ b/period_2/02-sockets/903/Signature.py @@ -0,0 +1,41 @@ +from cryptography.exceptions import * +from cryptography.hazmat.primitives.asymmetric import rsa +from cryptography.hazmat.primitives import hashes +from cryptography.hazmat.primitives.asymmetric import padding +from cryptography.hazmat.primitives import serialization + +def generate_keys(): + private_key = rsa.generate_private_key(public_exponent=65537,key_size=2048) + public_key = private_key.public_key() + + pbc_ser = public_key.public_bytes( + encoding=serialization.Encoding.PEM, + format=serialization.PublicFormat.SubjectPublicKeyInfo) + return private_key, pbc_ser + +def sign(message, private_key): + message = bytes(str(message), 'utf-8') + signature = private_key.sign( + message, + padding.PSS(mgf=padding.MGF1(hashes.SHA256()), salt_length=padding.PSS.MAX_LENGTH), + hashes.SHA256() + ) + return signature + +def verify(message, signature, pbc_ser): + message = bytes(str(message), 'utf-8') + public_key = serialization.load_pem_public_key(pbc_ser) + try: + public_key.verify( + signature, + message, + padding.PSS(mgf=padding.MGF1(hashes.SHA256()), + salt_length=padding.PSS.MAX_LENGTH), + hashes.SHA256() + ) + return True + except InvalidSignature: + return False + except: + print("Error executing 'public_key.verify'") + return False \ No newline at end of file diff --git a/period_2/02-sockets/903/SocketUtil.py b/period_2/02-sockets/903/SocketUtil.py new file mode 100644 index 0000000..827a8fd --- /dev/null +++ b/period_2/02-sockets/903/SocketUtil.py @@ -0,0 +1,41 @@ +import socket +import pickle +import select + +BUFFER_SIZE = 1024 + +def newServerSocket(ip_addr, port): + server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + server_socket.bind((ip_addr, port)) + server_socket.listen() + return server_socket + + +def recvObj(socket): + inputs, outputs, errs = select.select([socket], [], [socket], 6) + if socket in inputs: + connected_socket, addr = socket.accept() + all_data = b'' + while True: + data = connected_socket.recv(BUFFER_SIZE) + if not data: + break + all_data = all_data + data + return pickle.loads(all_data) + return None + + +def sendObj(ip_addr, blk, port): + soc = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + soc.connect((ip_addr, port)) + data = pickle.dumps(blk) + soc.send(data) + soc.close() + return False + + + + +# changes on newServerConnection +# We modified it to accept port number as a parameter, such that when +# we make a server we can specify on which port bind it \ No newline at end of file diff --git a/period_2/02-sockets/903/Transaction.py b/period_2/02-sockets/903/Transaction.py new file mode 100644 index 0000000..7f2fe0e --- /dev/null +++ b/period_2/02-sockets/903/Transaction.py @@ -0,0 +1,88 @@ +from Signature import * + +class Tx: + inputs = None + outputs =None + sigs = None + reqd = None + def __init__(self): + self.inputs = [] + self.outputs = [] + self.sigs = [] + self.reqd = [] + + def add_input(self, from_addr, amount): + self.inputs.append((from_addr, amount)) + + def add_output(self, to_addr, amount): + self.outputs.append((to_addr, amount)) + + def add_reqd(self, addr): + self.reqd.append(addr) + + def sign(self, private): + message = self.__gather() + newsig = sign(message, private) + self.sigs.append(newsig) + + def is_valid(self): + total_in = 0 + total_out = 0 + message = self.__gather() + for addr,amount in self.inputs: + found = False + for s in self.sigs: + if verify(message, s, addr): + found = True + if not found: + # print ("No good sig found for " + str(message)) + return False + if amount < 0: + return False + total_in = total_in + amount + for addr in self.reqd: + found = False + for s in self.sigs: + if verify(message, s, addr): + found = True + if not found: + return False + for addr,amount in self.outputs: + if amount < 0: + return False + total_out = total_out + amount + + # if total_out > total_in: + # # print("Outputs exceed inputs") + # return False + + return True + + def __gather(self): + data=[] + data.append(self.inputs) + data.append(self.outputs) + data.append(self.reqd) + return data + + def __repr__(self): + + repr_str = "INPUTS:\n" + for addr, amt in self.inputs: + repr_str = repr_str + str(amt) + "from" + str(addr) + "\n" + + repr_str += "OUTPUTS:\n" + for addr, amt in self.outputs: + repr_str = repr_str + str(amt) + "to" + str(addr) + "\n" + + repr_str += "EXTRA REQUIRED SIGNATURES:\n" + for req_sig in self.reqd: + repr_str = repr_str + str(req_sig) + "\n" + + repr_str += "SIGNATURES:\n" + for sig in self.sigs: + repr_str = repr_str + str(sig) + "\n" + + repr_str += "END\n" + + return repr_str diff --git a/period_2/02-sockets/903/TxBlock.py b/period_2/02-sockets/903/TxBlock.py new file mode 100644 index 0000000..0ed9e17 --- /dev/null +++ b/period_2/02-sockets/903/TxBlock.py @@ -0,0 +1,64 @@ +from BlockChain import CBlock +from Signature import generate_keys, sign, verify +from cryptography.hazmat.primitives import hashes +from cryptography.hazmat.backends import default_backend + +import random + +reward = 25.0 +leading_zeros = 2 +next_char_limit = 20 + +class TxBlock (CBlock): + + nonce = "A random nonce" + + def __init__(self, previousBlock): + super(TxBlock, self).__init__([], previousBlock) + + def addTx(self, Tx_in): + self.data.append(Tx_in) + + def count_totals(self): + total_in = 0 + total_out = 0 + for tx in self.data: + for addr, amt in tx.inputs: + total_in = total_in + amt + for addr, amt in tx.outputs: + total_out = total_out + amt + return total_in, total_out + + def is_valid(self): + if not super(TxBlock, self).is_valid(): + return False + for tx in self.data: + if not tx.is_valid(): + return False + total_in, total_out = self.count_totals() + if total_out - total_in - reward > 0.000000000001: + return False + return True + + def good_nonce(self): + digest = hashes.Hash(hashes.SHA256(), backend=default_backend()) + digest.update(bytes(str(self.data), 'utf8')) + digest.update(bytes(str(self.previousHash), 'utf8')) + digest.update(bytes(str(self.nonce), 'utf8')) + this_hash = digest.finalize() + + if this_hash[:leading_zeros] != bytes(''.join(['\x4f' for i in range(leading_zeros)]), 'utf8'): + return False + return int(this_hash[leading_zeros]) < next_char_limit + + def find_nonce(self): + for i in range(1000000): + self.nonce = ''.join([ + chr(random.randint(0, 255)) for i in range(10*leading_zeros)]) + if self.good_nonce(): + return self.nonce + return None + + + +# count_total() needs to be public to be used in Miner \ No newline at end of file diff --git a/period_2/02-sockets/903/Wallet_t.py b/period_2/02-sockets/903/Wallet_t.py new file mode 100644 index 0000000..13b7bae --- /dev/null +++ b/period_2/02-sockets/903/Wallet_t.py @@ -0,0 +1,69 @@ +from SocketUtil import * +from Transaction import * +from Signature import * + +M_SERVER_IP = 'localhost' +M_SERVER_PORT = 5005 + +W_SERVER_IP = 'localhost' +W_SERVER_PORT = 5006 + +alex_prv, alex_pbc = generate_keys() +mike_prv, mike_pbc = generate_keys() +rose_prv, rose_pbc = generate_keys() +mara_prv, mara_pbc = generate_keys() + +Tx1 = Tx() +Tx1.add_input(alex_pbc, 4.0) +Tx1.add_input(mike_pbc, 1.0) +Tx1.add_output(rose_pbc, 4.8) +Tx1.sign(alex_prv) +Tx1.sign(mike_prv) + +Tx2 = Tx() +Tx2.add_input(rose_pbc, 4.0) +Tx2.add_output(mike_pbc, 4.0) +Tx2.add_reqd(alex_pbc) +Tx2.sign(rose_prv) +Tx2.sign(alex_prv) + +print(Tx1.is_valid()) +print(Tx2.is_valid()) + +try: + sendObj(M_SERVER_IP, Tx1, M_SERVER_PORT) + print("Sent Tx1") + sendObj(M_SERVER_IP, Tx2, M_SERVER_PORT) + print("Sent Tx2") + +except: + print("Error! Connection unsuccessful.") + +wallet_server_socket = newServerSocket(W_SERVER_IP, W_SERVER_PORT) + +for i in range(10): + newBlock = recvObj(wallet_server_socket) + if newBlock: + break +wallet_server_socket.close() + +if newBlock.is_valid(): + print("Success! Block is valid.") + +if newBlock.good_nonce(): + print("Success! Nonce is valid.") + +tx1_found = tx2_found = False +for tx in newBlock.data: + try: + if tx.inputs[0][0] == alex_pbc and tx.inputs[0][1] == 4.0: + print("Tx1 is present.") + tx1_found = True + except: + pass + try: + if tx.inputs[0][0] == rose_pbc and tx.inputs[0][1] == 4.0: + print("Tx2 is present.") + tx2_found = True + except: + pass \ No newline at end of file diff --git a/rose.keys b/rose.keys deleted file mode 100644 index 17548a1..0000000 Binary files a/rose.keys and /dev/null differ