finished tutorials
This commit is contained in:
parent
c43218e0e3
commit
f58053b749
@ -0,0 +1,19 @@
|
|||||||
|
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()
|
@ -0,0 +1,67 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""Transactions -> Ledger (Transaction Blockchain): Tutorial 1
|
||||||
|
|
||||||
|
The goal of this tutorial is to learn how custom data structures can be de-/serialized
|
||||||
|
before storage and transference. In addition, you will learn how an application will fail
|
||||||
|
if serialization is not applied before storing an object in a file.
|
||||||
|
Any data structure needs to be serialized as a byte stream before storing it
|
||||||
|
in a file or transferring it through the network. Pickle module supports such an operation.
|
||||||
|
However, it has limited support for data structures to be serialized automatically.
|
||||||
|
Custom data structures such as asymmetric keys and transactions are not supported natively.
|
||||||
|
They need to be serialized before storage. Pickle will throw an exception if it
|
||||||
|
does not support the data structure. Some third-party modules such as Cryptography provide
|
||||||
|
such serialization functions. Only some objects are needed for storage or transference
|
||||||
|
should be serialized. For instance, we might need only to serialize public keys and
|
||||||
|
leave private keys not serialized. Depending on your implementation scenario,
|
||||||
|
you might also check JSON serialization functions.
|
||||||
|
|
||||||
|
Many serialization formats support multiple different types of asymmetric keys
|
||||||
|
and will return an instance of the appropriate type. You should check that
|
||||||
|
the returned key matches the type your application expects when using these methods.
|
||||||
|
|
||||||
|
In this scenario we generate a asymmetric keys. Use the private to sign a message.
|
||||||
|
Your task is to de-/serialize public key of alex before storing to and when loading from a file.
|
||||||
|
|
||||||
|
Your task is to:
|
||||||
|
* locate the TODOs in this file
|
||||||
|
* complete the missing part from the code
|
||||||
|
* run the test of this exercise located in same folder.
|
||||||
|
|
||||||
|
To test run 'KeyFile_test_a.py' in your command line
|
||||||
|
|
||||||
|
Notes:
|
||||||
|
* do not change class structure or method signature to not break unit tests
|
||||||
|
* visit this url for more information on this topic:
|
||||||
|
https://cryptography.io/en/latest/hazmat/primitives/asymmetric/serialization/
|
||||||
|
https://cryptography.io/en/latest/hazmat/primitives/asymmetric/serialization/#cryptography.hazmat.primitives.serialization.load_pem_public_key
|
||||||
|
"""
|
||||||
|
from Signature import *
|
||||||
|
from Transaction import *
|
||||||
|
import pickle
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
|
||||||
|
alex_prv, alex_pbc = generate_keys()
|
||||||
|
|
||||||
|
sample_message = b'a test message'
|
||||||
|
sig = sign(sample_message, alex_prv)
|
||||||
|
print(verify(sample_message, sig, alex_pbc))
|
||||||
|
|
||||||
|
savefile = open("key.dat", "wb")
|
||||||
|
|
||||||
|
# TODO 1: Serialize the public key to bytes
|
||||||
|
# Use PEM encoding
|
||||||
|
|
||||||
|
pickle.dump(alex_pbc, savefile)
|
||||||
|
savefile.close()
|
||||||
|
|
||||||
|
|
||||||
|
loadfile = open("key.dat", "rb")
|
||||||
|
new_pbc = pickle.load(loadfile)
|
||||||
|
|
||||||
|
# TODO 2: Deserialize a public key from PEM encoded data
|
||||||
|
# Load using one of the function that supports asymmetric public key-loading
|
||||||
|
|
||||||
|
|
||||||
|
loadfile.close()
|
||||||
|
print(verify(sample_message, sig, new_pbc))
|
@ -0,0 +1,35 @@
|
|||||||
|
from cryptography.exceptions import *
|
||||||
|
from cryptography.hazmat.primitives.asymmetric import rsa
|
||||||
|
from cryptography.hazmat.primitives import hashes
|
||||||
|
from cryptography.hazmat.primitives.asymmetric import padding
|
||||||
|
|
||||||
|
def generate_keys():
|
||||||
|
private_key = rsa.generate_private_key(public_exponent=65537,key_size=2048)
|
||||||
|
public_key = private_key.public_key()
|
||||||
|
return private_key, public_key
|
||||||
|
|
||||||
|
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, public_key):
|
||||||
|
message = bytes(str(message), 'utf-8')
|
||||||
|
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
|
@ -0,0 +1,66 @@
|
|||||||
|
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
|
@ -0,0 +1,76 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""Transactions -> Ledger (Transaction Blockchain): Tutorial 1
|
||||||
|
|
||||||
|
The goal of this tutorial is to learn how custom data structures can be de-/serialized
|
||||||
|
before storage and transference. In addition, you will learn how an application will fail
|
||||||
|
if serialization is not applied before storing an object in a file.
|
||||||
|
Any data structure needs to be serialized as a byte stream before storing it
|
||||||
|
in a file or transferring it through the network. Pickle module supports such an operation.
|
||||||
|
However, it has limited support for data structures to be serialized automatically.
|
||||||
|
Custom data structures such as asymmetric keys and transactions are not supported natively.
|
||||||
|
They need to be serialized before storage. Pickle will throw an exception if it
|
||||||
|
does not support the data structure. Some third-party modules such as Cryptography provide
|
||||||
|
such serialization functions. Only some objects are needed for storage or transference
|
||||||
|
should be serialized. For instance, we might need only to serialize public keys and
|
||||||
|
leave private keys not serialized. Depending on your implementation scenario,
|
||||||
|
you might also check JSON serialization functions.
|
||||||
|
|
||||||
|
In this scenario we try to store a custom data structure into a file without serialization.
|
||||||
|
Pickle will fail to store the byte stream into the file as it does not recognize the object.
|
||||||
|
Your task is to try different Pickle.dump() calls and check the output.
|
||||||
|
|
||||||
|
Your task is to:
|
||||||
|
* locate the TODOs in this file
|
||||||
|
* complete the missing part from the code
|
||||||
|
* run the test of this exercise located in same folder.
|
||||||
|
|
||||||
|
To test run 'TxFile_test_a.py' in your command line
|
||||||
|
|
||||||
|
Notes:
|
||||||
|
* do not change class structure or method signature to not break unit tests
|
||||||
|
* visit this url for more information on this topic:
|
||||||
|
https://cryptography.io/en/latest/hazmat/primitives/asymmetric/serialization/
|
||||||
|
https://cryptography.io/en/latest/hazmat/primitives/asymmetric/serialization/#cryptography.hazmat.primitives.serialization.load_pem_public_key
|
||||||
|
"""
|
||||||
|
from Signature import *
|
||||||
|
from Transaction import *
|
||||||
|
import pickle
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
|
||||||
|
# Generating asymmetric keys for multiple users
|
||||||
|
alex_prv, alex_pbc = generate_keys()
|
||||||
|
mike_prv, mike_pbc = generate_keys()
|
||||||
|
rose_prv, rose_pbc = generate_keys()
|
||||||
|
mara_prv, mara_pbc = generate_keys()
|
||||||
|
|
||||||
|
# Creating a transaction from alex to mike and signing it with alex private key
|
||||||
|
Tx1 = Tx()
|
||||||
|
Tx1.add_input(alex_pbc, 1)
|
||||||
|
Tx1.add_output(mike_pbc, 1)
|
||||||
|
Tx1.sign(alex_prv)
|
||||||
|
|
||||||
|
# Checking the validity of this transaction
|
||||||
|
if Tx1.is_valid():
|
||||||
|
print("Success! Tx is valid")
|
||||||
|
else:
|
||||||
|
print("Fail! Tx is invalid")
|
||||||
|
|
||||||
|
# Opening a file to store a transaction data
|
||||||
|
savefile = open("tx.dat", "wb")
|
||||||
|
|
||||||
|
# TODO 1: Try different dump() calls by uncommenting it
|
||||||
|
# Make sure you uncomment other dump calls before trying a new one
|
||||||
|
# Application should stop here as it fails to serialize our custom data structure (transaction)
|
||||||
|
pickle.dump(Tx1, savefile)
|
||||||
|
# pickle.dump(Tx1.inputs[0][0], savefile)
|
||||||
|
# pickle.dump(alex_pbc, savefile)
|
||||||
|
|
||||||
|
savefile.close()
|
||||||
|
|
||||||
|
loadfile = open("tx.dat", "rb")
|
||||||
|
newTx = pickle.load(loadfile)
|
||||||
|
|
||||||
|
if newTx.is_valid():
|
||||||
|
print("Sucess! Loaded tx is valid")
|
||||||
|
loadfile.close()
|
@ -0,0 +1,79 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""Transactions -> Ledger (Key Serialization): Tutorial 2
|
||||||
|
|
||||||
|
The goal of this tutorial is to learn how implement serialization mechanism into
|
||||||
|
different method to simplify your operation. Due to the scope limitation of this tutorial
|
||||||
|
we expect de-/serialization calls to be part of methods implementation (generate_keys() and verify()).
|
||||||
|
In an ideal scenario It would be more convenient to implement it in a more abstract way
|
||||||
|
as a part of your custom data structure
|
||||||
|
|
||||||
|
In this scenario we serialize only the generate public key of a user when keys are generated.
|
||||||
|
The private key remain as it is. When verifying the signature it is expected that the given public key
|
||||||
|
should be deserialized before usage.
|
||||||
|
It is expected that you rebuild Signature module to work with serialized keys
|
||||||
|
|
||||||
|
|
||||||
|
Your task is to:
|
||||||
|
* locate the TODOs in this file
|
||||||
|
* complete the missing part from the code
|
||||||
|
* run the test of this exercise located in same folder.
|
||||||
|
|
||||||
|
To test run 'Signature_t.py' in your command line
|
||||||
|
|
||||||
|
Notes:
|
||||||
|
* do not change class structure or method signature to not break unit tests
|
||||||
|
* visit this url for more information on this topic:
|
||||||
|
https://cryptography.io/en/latest/hazmat/primitives/asymmetric/serialization/
|
||||||
|
https://cryptography.io/en/latest/hazmat/primitives/asymmetric/serialization/#cryptography.hazmat.primitives.serialization.load_pem_public_key
|
||||||
|
"""
|
||||||
|
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
|
||||||
|
|
||||||
|
# TODO 1: Generate first a private key, then a public key. As a result return both values.
|
||||||
|
# Make sure you generate the keys in the correct order.
|
||||||
|
# Use recommended algorithm values where possible
|
||||||
|
# Suggested key size 2048
|
||||||
|
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
|
||||||
|
|
||||||
|
# TODO 2: Sign a passed message using the passed private key
|
||||||
|
# Signing and verifying algorithms must be the same
|
||||||
|
def sign(message, private_key):
|
||||||
|
# TODO 2: Sign a passed message using the passed private key
|
||||||
|
# Signing and verifying algorithms must be the same
|
||||||
|
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
|
||||||
|
|
||||||
|
# TODO 3: Verify a signature for a message with the passed public key
|
||||||
|
# Signing and verifying algorithms values must be the same
|
||||||
|
# Make sure to handle exception properly if verification fails
|
||||||
|
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:
|
||||||
|
return False
|
@ -0,0 +1,51 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""
|
||||||
|
This test case will verify if the provided solution by a student for Signature.py is correct.
|
||||||
|
"""
|
||||||
|
from Signature import *
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
# Generating asymmetric keys for both alex and mike
|
||||||
|
# returned public key of both mike and alex should be serialized
|
||||||
|
alex_prv, alex_pbc = generate_keys()
|
||||||
|
mike_prv, mike_pbc = generate_keys()
|
||||||
|
|
||||||
|
alex_message = b'pay 10 euro to bob'
|
||||||
|
|
||||||
|
|
||||||
|
# Verification of a signature using public key:
|
||||||
|
|
||||||
|
# It should be correct because Alex is signing it and we verifying using his public key
|
||||||
|
alex_signature = sign(alex_message, alex_prv)
|
||||||
|
|
||||||
|
verified = verify(alex_message, alex_signature, alex_pbc)
|
||||||
|
if verified:
|
||||||
|
print('Success: Correct signature is verified')
|
||||||
|
else:
|
||||||
|
print('Error: Correct signature is not verified')
|
||||||
|
|
||||||
|
# It should be correct because mike is signing it and we verifying using alex public key
|
||||||
|
f_signature = sign(alex_message, mike_prv)
|
||||||
|
|
||||||
|
verified = verify(alex_message, f_signature, alex_pbc)
|
||||||
|
if not verified:
|
||||||
|
print('Success: Wrong signature is not verified')
|
||||||
|
else:
|
||||||
|
print('Error: Wrong signature is verified')
|
||||||
|
|
||||||
|
# Check originality of message using public key:
|
||||||
|
# It should be original because alex is signing it and we verifying using alex public key
|
||||||
|
correct = verify(b'pay 10 euro to bob', alex_signature, alex_pbc)
|
||||||
|
if correct:
|
||||||
|
print('Sucsess: Original message is verified')
|
||||||
|
else:
|
||||||
|
print('Error: Original message is not verified')
|
||||||
|
|
||||||
|
|
||||||
|
# It should be tempered because alex signature of the message is different
|
||||||
|
t_message = b'pay 100 euro to bob'
|
||||||
|
correct = verify(t_message, alex_signature, alex_pbc)
|
||||||
|
if not correct:
|
||||||
|
print('Sucsess: Tampered message is detected')
|
||||||
|
else:
|
||||||
|
print('Error: Tampered message is not detected')
|
@ -0,0 +1,66 @@
|
|||||||
|
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
|
@ -0,0 +1,4 @@
|
|||||||
|
Success: Correct signature is verified
|
||||||
|
Success: Wrong signature is not verified
|
||||||
|
Sucsess: Original message is verified
|
||||||
|
Sucsess: Tampered message is detected
|
@ -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
|
@ -0,0 +1,83 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""Transactions -> Ledger (Transaction Save and Load): Tutorial 3
|
||||||
|
|
||||||
|
The goal of this tutorial is to learn how storing and loading a transaction from and to
|
||||||
|
a file system works. For this purpose you need to check the provided code in both files, Signatue.py and Transaction_t.py.
|
||||||
|
then, rebuild the transaction module to satisfy our testing scenario.
|
||||||
|
|
||||||
|
Your task is to:
|
||||||
|
* locate the TODOs in this file
|
||||||
|
* complete the missing part from the code
|
||||||
|
* run the test of this exercise located in same folder.
|
||||||
|
|
||||||
|
To test run 'Transaction_t.py' in your command line
|
||||||
|
|
||||||
|
Notes:
|
||||||
|
* do not change class structure or method signature to not break unit tests
|
||||||
|
* Check previous tutorials for more information on this topic:
|
||||||
|
"""
|
||||||
|
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
|
@ -0,0 +1,47 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""
|
||||||
|
This test case will verify if the provided solution by a student for TxBlock.py is correct.
|
||||||
|
"""
|
||||||
|
from Signature import generate_keys
|
||||||
|
|
||||||
|
from Transaction import Tx
|
||||||
|
|
||||||
|
import pickle
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
|
||||||
|
# Generating asymmetric keys for multiple users
|
||||||
|
alex_prv, alex_pbc = generate_keys()
|
||||||
|
mike_prv, mike_pbc = generate_keys()
|
||||||
|
rose_prv, rose_pbc = generate_keys()
|
||||||
|
mara_prv, mara_pbc = generate_keys()
|
||||||
|
|
||||||
|
# Creating a transaction from alex to mike and signing it with alex private key
|
||||||
|
Tx1 = Tx()
|
||||||
|
Tx1.add_input(alex_pbc, 1)
|
||||||
|
Tx1.add_output(mike_pbc, 1)
|
||||||
|
Tx1.sign(alex_prv)
|
||||||
|
|
||||||
|
# Checking the validity of this transaction
|
||||||
|
if Tx1.is_valid():
|
||||||
|
print("Success! Tx is valid")
|
||||||
|
else:
|
||||||
|
print("Fails!")
|
||||||
|
|
||||||
|
# Storing transaction data in a file
|
||||||
|
# The operation should succeed this time
|
||||||
|
savefile = open("tx.dat", "wb")
|
||||||
|
pickle.dump(Tx1, savefile)
|
||||||
|
savefile.close()
|
||||||
|
|
||||||
|
# Loading transaction data from a file
|
||||||
|
# The operation should succeed this time
|
||||||
|
loadfile = open("tx.dat", "rb")
|
||||||
|
newTx = pickle.load(loadfile)
|
||||||
|
|
||||||
|
# Verifying the validity of the transaction
|
||||||
|
if newTx.is_valid():
|
||||||
|
print("Sucess! Loaded tx is valid")
|
||||||
|
else:
|
||||||
|
print("Fails!")
|
||||||
|
loadfile.close()
|
@ -0,0 +1,2 @@
|
|||||||
|
Success! Tx is valid
|
||||||
|
Sucess! Loaded tx is valid
|
@ -0,0 +1,19 @@
|
|||||||
|
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()
|
@ -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
|
@ -0,0 +1,66 @@
|
|||||||
|
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
|
@ -0,0 +1,50 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""Transactions -> Ledger (Transaction and Blocks): Tutorial 4
|
||||||
|
|
||||||
|
The goal of this tutorial is to learn how a blockchain for the transactions is implemented.
|
||||||
|
In this scenario the implementation of the block is minimal. Each block contains only his
|
||||||
|
own hash value, transaction data and the hash value of previous block. Check the provided
|
||||||
|
code in both files, Signature.py, Transaction.py and Blockchain.py. then,
|
||||||
|
rebuild the Block module to satisfy our testing scenario.
|
||||||
|
|
||||||
|
Your task is to:
|
||||||
|
* locate the TODOs in this file
|
||||||
|
* complete the missing part from the code
|
||||||
|
* run the test of this exercise located in same folder.
|
||||||
|
|
||||||
|
To test run 'TxBlock_t.py' in your command line
|
||||||
|
|
||||||
|
Notes:
|
||||||
|
* do not change class structure or method signature to not break unit tests
|
||||||
|
* Check previous tutorials for more information on this topic
|
||||||
|
"""
|
||||||
|
from BlockChain import CBlock
|
||||||
|
from Signature import generate_keys, sign, verify
|
||||||
|
from Transaction import Tx
|
||||||
|
|
||||||
|
class TxBlock (CBlock):
|
||||||
|
previousHash = None
|
||||||
|
previousBlock = None
|
||||||
|
data = None
|
||||||
|
# TODO 1: Initialize the block
|
||||||
|
# Each block contains a list for the data and a hash value to previous block
|
||||||
|
def __init__(self, previousBlock):
|
||||||
|
self.previousBlock = previousBlock
|
||||||
|
self.data = []
|
||||||
|
if previousBlock != None:
|
||||||
|
self.previousHash = previousBlock.computeHash()
|
||||||
|
# TODO 1: Initialize the block
|
||||||
|
# Each block contains a list for the data and a hash value to previous block
|
||||||
|
|
||||||
|
|
||||||
|
# TODO 2: Append the transaction to the data list
|
||||||
|
def addTx(self, Tx_in):
|
||||||
|
self.data.append(Tx_in)
|
||||||
|
|
||||||
|
# TODO 3: Check the validity of each transaction in the data list
|
||||||
|
# Expected return value is true or false
|
||||||
|
def is_valid(self):
|
||||||
|
for t in self.data:
|
||||||
|
if t.is_valid() == False:
|
||||||
|
return False
|
||||||
|
return True
|
@ -0,0 +1,117 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""
|
||||||
|
This test case will verify if the provided solution by a student for TxBlock.py is correct.
|
||||||
|
In this test scenario 6 blocks will be created (1 genesis and 5 child blocks).
|
||||||
|
A total of 8 transactions will be created. 3 of those transactions are invalid.
|
||||||
|
Every time we add some transactions to a block and add the block
|
||||||
|
to a chain, we iterate through the chain and check if those transactions are valid.
|
||||||
|
|
||||||
|
"""
|
||||||
|
from BlockChain import CBlock
|
||||||
|
from Signature import generate_keys, sign, verify
|
||||||
|
from Transaction import Tx
|
||||||
|
import pickle
|
||||||
|
from cryptography.hazmat.primitives import serialization
|
||||||
|
from cryptography.hazmat.backends import default_backend
|
||||||
|
|
||||||
|
from TxBlock import *
|
||||||
|
|
||||||
|
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()
|
||||||
|
|
||||||
|
# Valid Transactions
|
||||||
|
####################
|
||||||
|
# Create 2 valid transactions Tx1 and Tx2
|
||||||
|
Tx1 = Tx()
|
||||||
|
Tx1.add_input(alex_pbc, 1)
|
||||||
|
Tx1.add_output(rose_pbc, 1)
|
||||||
|
Tx1.sign(alex_prv)
|
||||||
|
|
||||||
|
Tx2 = Tx()
|
||||||
|
Tx2.add_input(mike_pbc,1.1)
|
||||||
|
Tx2.add_output(rose_pbc, 1)
|
||||||
|
Tx2.sign(mike_prv)
|
||||||
|
|
||||||
|
# Add Tx1 and Tx2 to the first block (genesis block)
|
||||||
|
root = TxBlock(None)
|
||||||
|
root.addTx(Tx1)
|
||||||
|
root.addTx(Tx2)
|
||||||
|
|
||||||
|
# Create 2 more valid transactions Tx3 and Tx4
|
||||||
|
Tx3 = Tx()
|
||||||
|
Tx3.add_input(rose_pbc,1.1)
|
||||||
|
Tx3.add_output(alex_pbc, 1)
|
||||||
|
Tx3.sign(rose_prv)
|
||||||
|
|
||||||
|
Tx4 = Tx()
|
||||||
|
Tx4.add_input(mike_pbc,1)
|
||||||
|
Tx4.add_output(mara_pbc, 1)
|
||||||
|
Tx4.add_reqd(rose_pbc)
|
||||||
|
Tx4.sign(mike_prv)
|
||||||
|
Tx4.sign(rose_prv)
|
||||||
|
|
||||||
|
# Add Tx3 and Tx4 to the second block (the child of the genesis block)
|
||||||
|
B1 = TxBlock(root)
|
||||||
|
B1.addTx(Tx3)
|
||||||
|
B1.addTx(Tx4)
|
||||||
|
|
||||||
|
for b in [root, B1]:
|
||||||
|
if b.is_valid():
|
||||||
|
print ("Success! Valid block is verified.")
|
||||||
|
else:
|
||||||
|
print ("Error! Valid block is not verified.")
|
||||||
|
|
||||||
|
# Create new valid transaction Tx5
|
||||||
|
Tx5 = Tx()
|
||||||
|
Tx5.add_input(rose_pbc, 2.3)
|
||||||
|
Tx5.add_output(mike_pbc, 2.3)
|
||||||
|
Tx5.sign(rose_prv)
|
||||||
|
# Create a new block (a child of the block B1), and the transaction Tx5 to the block
|
||||||
|
B2 = TxBlock(B1)
|
||||||
|
B2.addTx(Tx5)
|
||||||
|
|
||||||
|
# Add another valid transaction to B1
|
||||||
|
B1.addTx(Tx4)
|
||||||
|
|
||||||
|
for b in [root, B1, B2]:
|
||||||
|
if b.is_valid():
|
||||||
|
print ("Success! Valid block is verified.")
|
||||||
|
else:
|
||||||
|
print ("Error! Valid block is not verified.")
|
||||||
|
|
||||||
|
|
||||||
|
# Invalid Transactions
|
||||||
|
######################
|
||||||
|
# Create an invalid transaction Tx6
|
||||||
|
Tx6 = Tx()
|
||||||
|
Tx6.add_input(mara_pbc, 2.0)
|
||||||
|
Tx6.add_output(rose_pbc, 15.3)
|
||||||
|
Tx6.sign(mara_prv)
|
||||||
|
B3 = TxBlock(B2)
|
||||||
|
B3.addTx(Tx6)
|
||||||
|
|
||||||
|
# Creat an invalid transaction Tx7
|
||||||
|
Tx7 = Tx()
|
||||||
|
Tx7.add_input(rose_pbc, 2.3)
|
||||||
|
Tx7.add_output(mike_pbc, 2.3)
|
||||||
|
Tx7.sign(mike_prv)
|
||||||
|
B4 = TxBlock(B3)
|
||||||
|
B4.addTx(Tx7)
|
||||||
|
|
||||||
|
# Creat an invalid transaction Tx8
|
||||||
|
Tx8 = Tx()
|
||||||
|
Tx8.add_input(mike_pbc, 0.9)
|
||||||
|
Tx8.add_output(mara_pbc, 0.8)
|
||||||
|
Tx8.add_reqd(rose_pbc)
|
||||||
|
Tx8.sign(mike_prv)
|
||||||
|
B5 = TxBlock(B4)
|
||||||
|
B5.addTx(Tx8)
|
||||||
|
|
||||||
|
for b in [B3, B4, B5]:
|
||||||
|
if b.is_valid():
|
||||||
|
print ("Error! Invalid block is verified.")
|
||||||
|
else:
|
||||||
|
print ("Success! Invalid blocks is detected.")
|
@ -0,0 +1,8 @@
|
|||||||
|
Success! Valid block is verified.
|
||||||
|
Success! Valid block is verified.
|
||||||
|
Success! Valid block is verified.
|
||||||
|
Success! Valid block is verified.
|
||||||
|
Success! Valid block is verified.
|
||||||
|
Success! Invalid blocks is detected.
|
||||||
|
Success! Invalid blocks is detected.
|
||||||
|
Success! Invalid blocks is detected.
|
Loading…
x
Reference in New Issue
Block a user