finished HW and EX

This commit is contained in:
Spekulaas 2023-10-03 13:57:03 +02:00
parent 8edf706e22
commit fb8a7d5b69
12 changed files with 689 additions and 0 deletions

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -0,0 +1,57 @@
#!/usr/bin/env python3
"""Transactions -> Ledger (Block Validation): Exercise
The goal of this exercise is to learn how a blockchain for the transactions is implemented.
In this scenario the implementation of the block is extended with a validation function for the block.
Each block contains 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.
In Blockchain.py the is_valid() method is provided to check the validity of the block,
rebuild the Block module to satisfy our testing scenario.
The testing scenario here covers tempering the data of one block.
This tempering should be detectable.
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: Init
# TODO 1: Initialize the block
# Each block contains a list for the data and a hash value to previous block
def __init__(self, previousBlock):
# TODO 1: Initialize the block
# Each block contains a list for the data and a hash value to previous block
self.previousBlock = previousBlock
self.data = []
if previousBlock != None:
self.previousHash = previousBlock.computeHash()
def addTx(self, Tx_in):
self.data.append(Tx_in)
# TODO 3: Check the validity of each transaction in the data list
# and check the validity of other blocks in the chain to make the cchain tamper-proof
# Expected return value is true or false
def is_valid(self):
if self.previousBlock != None:
if self.previousBlock.computeHash() != self.previousHash:
return False
for t in self.data:
if Tx.is_valid(t) == False:
return False
return True

View File

@ -0,0 +1,110 @@
#!/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.
Tempering the data on block b1 by adding a valid transaction to it should be detected.
"""
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 Blocks
####################
# 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)
# Creat 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)
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 Blocks
######################
# Creat 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)
# Tamper the block before B1 by adding a valid transaction to it
# This will make the block B1 (the block after the tampered block) invalid
B2.previousBlock.addTx(Tx4)
for b in [B2, B3, B4, B5]:
if b.is_valid():
print ("Error! Invalid block is verified.")
else:
print ("Success! Invalid blocks is detected.")

View File

@ -0,0 +1,7 @@
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.
Success! Invalid blocks is detected.

View File

@ -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

View File

@ -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

View File

@ -0,0 +1,104 @@
#!/usr/bin/env python3
"""
The goal of this exercise is to complete the transaction module.
In this exercise you need to add a __repr__() function that will be used
to show the details of transaction.
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 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
# TODO :
# Complete __repr__() method.
# for the desired format, check the 'output.txt' file.
def __repr__(self):
repr = ""
repr += "INPUTS:\n"
for addr, amount in self.inputs:
repr += str(amount) + " from " + str(addr) + "\n"
repr += "OUTPUTS:\n"
for addr, amount in self.outputs:
repr += str(amount) + " to " + str(addr) + "\n"
repr += "REQD:\n"
for addr in self.reqd:
repr += str(addr) + "\n"
repr += "SIGS:\n"
for sig in self.sigs:
repr += str(sig) + "\n"
return repr

View File

@ -0,0 +1,57 @@
#!/usr/bin/env python3
"""
The goal of this exercise is to complete the TXBlock class. We want be able to correctly store and
load a block on disk. You have already completed every required modules seperately in the previous tutorials.
In this exercise, you have to integrate all the previously created modules, and ensure
all components are properly working together.
In addition, we would also like to see details of a transaction. For this part, check the assignment
for Transaction.py 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 '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: Init
# TODO 1: Initialize the block
# Each block contains a list for the data and a hash value to previous block
def __init__(self, previousBlock):
# TODO 1: Initialize the block
# Each block contains a list for the data and a hash value to previous block
self.previousBlock = previousBlock
self.data = []
if previousBlock != None:
self.previousHash = previousBlock.computeHash()
def addTx(self, Tx_in):
self.data.append(Tx_in)
# TODO 3: Check the validity of each transaction in the data list
# and check the validity of other blocks in the chain to make the cchain tamper-proof
# Expected return value is true or false
def is_valid(self):
if self.previousBlock != None:
if self.previousBlock.computeHash() != self.previousHash:
return False
for t in self.data:
if Tx.is_valid(t) == False:
return False
return True

View File

@ -0,0 +1,129 @@
#!/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.
Tempering the data on block b1 by adding a valid transaction to it should be detected.
In addition, Transaction should be printed formmated and human-readable. You shoud add a method
to transaction file to represent a transaction in the desired format.
For the correct format check the 'output.txt' file.
"""
from BlockChain import CBlock
from Signature import generate_keys, sign, verify
import pickle
from cryptography.hazmat.primitives import serialization
from cryptography.hazmat.backends import default_backend
# from Transaction import Tx
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 Blocks
####################
# 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)
# Save the block B1 on the disk
savefile = open("block.dat", "wb")
pickle.dump(B1, savefile)
savefile.close()
# Creat 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)
# Load the block B1 from the disk
loadfile = open("block.dat" ,"rb")
load_B1 = pickle.load(loadfile)
loadfile.close()
for b in [root, B1, B2, load_B1]:
if b.is_valid():
print ("Success! Valid block is verified.")
else:
print ("Error! Valid block is not verified.")
# Invalid Blocks
######################
# Creat 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)
# Tamper the block before B1 by adding a valid transaction to it
# This will make the block B1 (the block after the tampered block) invlaid
B1.previousBlock.addTx(Tx4)
for b in [B1, B3, B4, B5]:
if b.is_valid():
print("Error! Invalid block is verified.")
else:
print("Success! Invalid blocks is detected.")
print('Tx4\n', Tx4)
print('Tx5\n', Tx5)

View File

@ -0,0 +1,29 @@
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.
Success! Invalid blocks is detected.
Tx4
INPUTS:
1 from b'-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA17Qc21PHEjqP7CLg1v1l\nYaSRy4gu5MXWcjbflmInox6IbMD/7P37kuvWRw/ruVCidoy8JmwasDGDv6Z9vRfN\nYSIssLziMcbhWUdEMLlI5Qu1Vf1eUzAxUEhQ4SBUi21BYX5uKoZm+rzd/idTi00W\nLI9c3/mKx5+i8pUBNFOXAijlqr5akxmqox2CN5cynKt2YK3ADOJPY8+hFT7s99Vd\np/3EWf4hyFWXWSnMTCs+n42pYf5ZA3/kRFzMI8x070zfNNqjgseXQuyc0br4U1Jt\nnAJaE4OzKez4rEF1tyZ9YBxX+D1ZshZpozdLxIyO4JUa4913zHFlz6CcIMH3a1hI\nVQIDAQAB\n-----END PUBLIC KEY-----\n'
OUTPUTS:
1 to b'-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAxGLdr2pV+nnQBH7oDP/9\nnBx5rc8zQJAsXSyL/HQAb+x3bhxRditCqsrShEIZpix7OZmMy8R8C8R+M6OAmeMy\nKGXxGD80WINSJgzSy2SZwAbKVNCzFEPVg/5tNgAzZBkeAZ8ddgrFzago2eeJwjyr\nthcdfnlJzw+LIXkIbG0AmU9XGRE3HpxlY0k5BJZ1LoWVezq+ip/nAO2i0Ht/rGxN\njmJvvDjGkawaHbeNIGdhEqIJp2/sKCJg0OyU7r1QbAwlbF61fEyzCVEH+PrqHmlI\nnCrP/qkxr/7bcWADbM2rX1sB8ueSAu70EIPeFtdGCDr+vzc7qphelFnzK8ekwiFD\nYQIDAQAB\n-----END PUBLIC KEY-----\n'
EXTRA REQUIRED SIGNATURES:
b'-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA1HKkpz+xIVe4B7oT7I+V\nx6FYBlT0H9BRUdOnYHKaUVNdR8WnlJBFzdl5oDOOfpzvtJdQtM6RD1lKPdKhvelE\nlemnTqBKT4aeZtSrbmTHhbJgS7cgyVTi3w2Ct7ye/ypyMPoFZlLCI7L7UGyzLKlQ\npnOJTouYzwgN7m7T3IaAYYwrRwCMczq+IKARPPfA8wMcdb1/tM7voqGmiSb5Z0fz\nHsd/MWcKuL3ZGBkx3lT2QEXuTT3OO6V8LdVWMU0kEaM0lS30KzVIT7CNmbyBe7kc\nsBnZ0Q79GlQaLnsDphPUrbndsr3jp4Wmx0H9JzLNNnJrvmOnWKTgHi5BTylVxMe7\nWQIDAQAB\n-----END PUBLIC KEY-----\n'
SIGNATURES:
b'G\xcb\xc0\xf4ULc|~4#{\xc7\xf2\x8f:\xa7\xef\xb5H@&\xaaQ,K/\x9b\x92\xaa\xbd\xa7\xe66\xf5q\xf1\x90XD\xf5>AO\x19\x11\x9f\x89\x0b\xcc\xb9]Zu\xf0x\xe8k&\x8b\xb6k\xeb9\x81\x9a\x0b\xf7\\yl\xdcc7\xfbP\xd5W\xb5\xa2\x1bY\x1a\\d\xfe\xe8.\xf9\xecG%\xc9\xcb\x1ab\xee<nu\x08<\xe9\x90X\xd6\xa6qd\xd6#\xcd\x13\xc5I\x99\xd7\xfc\xaaP\xf4t\xf7\x14\x92\xcbu\xa2\xdeE\x19\xd3\x82\xb4S\xa3L\xe4\xe8\x85\x92\x12O\xd3@H\xf4OR\x0b\x82\tI\x9eR\xc1xW\xbe8\x82\\\xaf\x00\xf5\xc5\xbfN\xc3\xcde\xc7\xd2\x93\xba\x11\xf7\xf9]\xf6\xb1\xbb(7\xf8H\xec\xbb\xf1Y\xc5\xd6q\xb21\xe5\x90"7 \xe1\')\x8e\x0c\xae\x9a\xc8\x05*hF\x86\xed\x12\x08\xe2\x96U \xcb\xea\xe2\xed\xean\x12\xc9\xd1\x80\xdb\xd6\xf7\xffJ\x19\xdeTv5!\xaf\xee\x95\xf3\xa0\xc3g\xe7\xc6e\xa2,E\xf0\x16'
b'\x05\xf0\xd3\xd1\xde^\xceB\xb6\x03.\xcf\x92`\xc4\xe3\x9d%\xc1\xce\xaa\xfao\x80\xe77\xfcg\x7f\x8a\xbb\x87\x1fz\x81\xad\x96\xc64\x07\xd9%\x01f\x04\xf1\xfa\xa5\xad?\xf5I9\x9de%\xe8\xf13\x1c.w\x0eT\x10hb\x04\x1c\xf9\xe5\x1b!\x99\xb5fr\xe3\x87\xc3?\xec\xe7^;w\xb3\xcdT\xd6\x82Zb\xa7 \x9e\x85k8\xf5\x98\xf6\x9d\xf3\xaf\x88^\x8f5\xe8\xa4}\xa4Q@G\x1f\xa8\xf6\xcf\xdbG\x8fHG\xe7x\\\xb7\xf6\xed\xe1\x03?\x81\xcavS_\xcc\xc0\xa6\xf0\x99\xaa\x86ZZQ\xebI*6\xaf!\xec\xe9l\x9f\x13\x93\xf4\xce\x94,\xd0.\x19`\xefV\x87/l#x\x84<%\x19\xba\xa8\x12\xec\xd3\x88_vW\x9ak\xa60\t\x80k\xda\xf4\xd2\xf6\xa0b\x9e]\xe1N\x06\xbd\xe5J\xc8\x7f\\`\x01]Q\xbf\xa8\x83\xc0\xab\xe1\x8c\xc3\x7f\xcf\xbf\xd2s#\xb5&,gP\x03\xb4\x95\xe5\xdf\xf7I@\xdb\xe4$\xe76l\xa4\xbeMN59'
END
Tx5
INPUTS:
2.3 from b'-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA1HKkpz+xIVe4B7oT7I+V\nx6FYBlT0H9BRUdOnYHKaUVNdR8WnlJBFzdl5oDOOfpzvtJdQtM6RD1lKPdKhvelE\nlemnTqBKT4aeZtSrbmTHhbJgS7cgyVTi3w2Ct7ye/ypyMPoFZlLCI7L7UGyzLKlQ\npnOJTouYzwgN7m7T3IaAYYwrRwCMczq+IKARPPfA8wMcdb1/tM7voqGmiSb5Z0fz\nHsd/MWcKuL3ZGBkx3lT2QEXuTT3OO6V8LdVWMU0kEaM0lS30KzVIT7CNmbyBe7kc\nsBnZ0Q79GlQaLnsDphPUrbndsr3jp4Wmx0H9JzLNNnJrvmOnWKTgHi5BTylVxMe7\nWQIDAQAB\n-----END PUBLIC KEY-----\n'
OUTPUTS:
2.3 to b'-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA17Qc21PHEjqP7CLg1v1l\nYaSRy4gu5MXWcjbflmInox6IbMD/7P37kuvWRw/ruVCidoy8JmwasDGDv6Z9vRfN\nYSIssLziMcbhWUdEMLlI5Qu1Vf1eUzAxUEhQ4SBUi21BYX5uKoZm+rzd/idTi00W\nLI9c3/mKx5+i8pUBNFOXAijlqr5akxmqox2CN5cynKt2YK3ADOJPY8+hFT7s99Vd\np/3EWf4hyFWXWSnMTCs+n42pYf5ZA3/kRFzMI8x070zfNNqjgseXQuyc0br4U1Jt\nnAJaE4OzKez4rEF1tyZ9YBxX+D1ZshZpozdLxIyO4JUa4913zHFlz6CcIMH3a1hI\nVQIDAQAB\n-----END PUBLIC KEY-----\n'
EXTRA REQUIRED SIGNATURES:
SIGNATURES:
b'\xb7\xdc\xe9\xe0\xb7\x848\xc22dy^o\xdd/\xacQ\x04?<.\x05\xdc\x00\xde\xf4\x1aH\xa2 ?z\xca\x96f\x0c\xa7\xe7v\xbf.\x8f/\x97@<\xba\xbf\xb6\xa4\xfc\xd9\xc0U\n6B\xb1t\x16\xc5W&`\xd6Bo\x91*\xd0o\xd9*r\x8b\xba\x8e(\xaeT\x9b0\xf7\x9f\xb4hRW\x08\xf2\xd3m\x18\xc3\xca\x13C\xeb\x98\x81\x1cWz]}\x9b\xc4\xc3\xc0\x8egmf\n\xbej\xe6\xf0owN\xb8\xaaw\x16X\xac\xa7\xe6\x87\xc3\x8f\xa1_\xff\xc7\xaa\xa5)\xa9\xe3x\x0eEI\xe0\xff\x18\xbc\xb1p\xe6\xc9\x11\x04\x86\x027\x9e\xa01\xb2\x13\xd1tr\x17Nv\xee\xa3\x84\xf8C\\\x0c\xa1\x08x\xa9\x8d\xd0t\xad!\x17d\xcb\x1f\xaaoY\xbb\x1ew\xb0UU,lnR\x95M\x1f\x1cLl0\xe0\x96S\x0e)\xf4q\xff\xd6\x83\x97\xaa\xc7\x81\xeaM\xc9%]\x06]\x02J\x11\x073\xe60\x05\xd0\xf2\xe3\xe1,r\xfb\x1aV7]\t\xea\xf7\xf5\xe0\xfes'
END