The following Jupyter Notebook, available [here], contains almost all available features.
This is a demo notebook for the bcbcpy package.
To install it, run
pip install git+https://github.com/aheritianad/BootCamp-BlockChain-and-Python.git --use-pep517
try:
import bcbcpy
except ModuleNotFoundError:
import os, sys
sys.path.append(os.path.abspath(".."))
import bcbcpy
Cryptography¶
from bcbcpy import crypto
Hash¶
Hash
function¶
- $\mathfrak{h}(x) = \mathfrak{h}(y)$ only if $x=y$.
- given $h\in H$, it is not easy to find $x\in X$ such that $\mathfrak{h}(x) = h$.
In some sense, $\mathfrak{h}(x)$ is a fingerprint of $x$.
text_to_be_hashed = "Hello World"
first_hash = crypto.hash_function(text_to_be_hashed, hash_name=crypto.HashName.sha256)
first_hash
'1370eeaaba7a6c7a234b1f82cc3b6d013a0088fd5e16408300f05b28b0015463'
crypto.hash_function(text_to_be_hashed + "!")
'e59f8bdf1305e382a4919ccefd613d3eebae612aa4c443f3af2d65663de3b075'
crypto.hash_function("Hello World!")
'e59f8bdf1305e382a4919ccefd613d3eebae612aa4c443f3af2d65663de3b075'
long_text = """
Hello everyone!
Welcome to this tutorial.
I promise that I will do my best to help you.
It will be interactive, so please participate as much as you can.
Please do not hesitate to ask if there you have questions.
Hope you will enjoy it!
Cheers!
Heritiana.
"""
hash_long = crypto.hash_function(long_text)
hash_long
'0c8359860bef1b26f811bf8448c546c2241c7c695f9b8ee76c1a8bf2d5b2dff3'
len(hash_long) == len(first_hash)
True
Validation¶
difficulty = 4
crypto.is_valid_hash(first_hash, difficulty)
False
fake_hash = "0000" + first_hash[4:]
fake_hash
'0000eeaaba7a6c7a234b1f82cc3b6d013a0088fd5e16408300f05b28b0015463'
crypto.is_valid_hash(fake_hash, difficulty)
True
Nonce¶
second_hash, nonce = crypto.hash_nonce_initializer(
text_to_be_hashed, difficulty=difficulty
)
second_hash
'0000bfe6af4232f78b0c8eba37a6ba6c17b9b8671473b0b82305880be077edd9'
nonce
107105
crypto.is_valid_hash(second_hash, difficulty)
True
third_hash = crypto.hash_function(text_to_be_hashed, nonce=nonce)
third_hash
'0000bfe6af4232f78b0c8eba37a6ba6c17b9b8671473b0b82305880be077edd9'
second_hash == third_hash
True
forth_hash = crypto.hash_function(text_to_be_hashed, nonce, nonce="") # type:ignore
second_hash == forth_hash
True
Q: What if such nonce
does not exist?¶
Think about it.
Cipher¶
Encryption
& Decryption
¶
encrypt = lambda x: 2 * x - 1
decrypt = lambda x: 0 # TODO to fill
import random
try:
for i in range(20):
x = random.randint(-5000, 5000)
assert (
encrypt(decrypt(x)) == decrypt(encrypt(x)) == x
), f"Oops! fail at {i+1}-th attempt for x = {x}."
except AssertionError as e:
print(e)
else:
print("Congrats! I passes the test.")
Oops! fail at 1-th attempt for x = -4995.
clear_message = (
"This is a clear message to encrypt and decrypt. I add this to make it longer."
)
Types¶
Key¶
$\mathcal{F}=\{ f_\theta \:\ \theta \in \Theta\}$
$\left(f_{\theta}\right) ^{-1} = f_{\theta^\prime}$ for some $\theta^\prime \in \Theta$
$\theta \in \Theta$ : key
Symmetric¶
Given $f_\theta$ or $\theta\in \Theta$ (thus $f_\theta$), one can construct easily $\left(f_\theta\right)^{-1}$.
From Wikipedia (2023-05-01):
Symmetric-key algorithms are algorithms for cryptography that use the same cryptographic keys for both the encryption of plaintext and the decryption of ciphertext. The keys may be identical, or there may be a simple transformation to go between the two keys.
Affine¶
$N$ : number of character.
key: $ \theta \in \mathbb{Z} \times \mathbb{Z}$
function: $f_{(a,b)}: x \mod N \mapsto (ax + b) \mod N$
inverse: $\theta^\prime = (a^{-1}\mod N, -a^{-1}b\mod N)$ for $\theta = (a,b)$.
affine_key = crypto.AffineKey.generate_key()
print(affine_key)
AffineKey(70,34)
crypto.AffineKey.compute_inverse((6,3))
(81, 48)
encr_affine = affine_key.encrypt(clear_message)
print(
f"""
original:
========
{clear_message}
Encrypted:
========
{encr_affine}
"""
)
original: ======== This is a clear message to encrypt and decrypt. I add this to make it longer. Encrypted: ======== =gLakLakbk,\Wb|kAWaab!WkFlkW&,| QFkb&rkrW,| QFukCkbrrkFgLakFlkAbwWkLFk\l&!W|u
decr = affine_key.decrypt(encr_affine)
print(
f"""
original:
========
{clear_message}
Decrypted:
=========
{decr}
"""
)
original: ======== This is a clear message to encrypt and decrypt. I add this to make it longer. Decrypted: ========= This is a clear message to encrypt and decrypt. I add this to make it longer.
Permutation¶
crypto.PermutationKey.compute_inverse([2, 3, 4, 1])
[4, 1, 2, 3]
Idea1: Chunking¶
Chunk input with a chunk size equal to permutation length and apply permutation on each chunks.
perm_key = crypto.PermutationKey.generate_key(length=3, n_runs=1)
print(perm_key)
PermutationKey([2, 3, 1])
encr_perm = perm_key.encrypt(clear_message)
print(
f"""
original:
========
{clear_message}
Encrypted:
========
{encr_perm}
"""
)
original: ======== This is a clear message to encrypt and decrypt. I add this to make it longer. Encrypted: ======== hiT is ascl areme sase go tnceypr atd necdypr. t aId dhit ts mokeait lo genr.
decr = perm_key.decrypt(encr_perm)
print(
f"""
original:
========
{clear_message}
Decrypted:
=========
{decr}
"""
)
original: ======== This is a clear message to encrypt and decrypt. I add this to make it longer. Decrypted: ========= This is a clear message to encrypt and decrypt. I add this to make it longer.
Idea2: Sliding¶
One can think about sliding the permutation like the following.
Suppose we have the following permutation
$$\theta: \begin{cases}1\mapsto 3\\ 2\mapsto 2\\ 3\mapsto 1\end{cases}$$
and input abcdefgh
.
input = abcdefgh
abc defgh
$\to$cba defgh
c bad efgh
$\to$c dab efgh
cd abe fgh
$\to$cd eba fgh
cde baf gh
$\to$cde fab gh
cdef abg h
$\to$cdef gba h
cdefg bah
$\to$cdefg hab
output = cdefghab
⚠️: the same process does not bring back input
from output
It can be fixed by doing a tricky thing to the previous output to get the final
output.
You can think about it and its implementation.
Adding noise¶
from bcbcpy import utils
noisy_message = utils.add_noises(clear_message)
noisy_message
'T<hHi.sb 3i!s5 waw Zc9l|esaVrk Km+eHs$sQaKg\te, Vt_o" 5e7nCc5rQylpRtA na2nxdL TdkeacfryyYp]t\'.b 7I\t Uacd<dH otRhZi,sW ut?o6 1m]a;kEe_ &i$t4 BlhoanWg`e\tr\'.Z'
noisy_message[::2]
'This is a clear message to encrypt and decrypt. I add this to make it longer.'
utils.remove_noises(noisy_message)
'This is a clear message to encrypt and decrypt. I add this to make it longer.'
encr_noisy = affine_key.encrypt(noisy_message)
decr_noisy = affine_key.decrypt(encr_noisy)
decr_without_noise = utils.remove_noises(decr_noisy)
print(
f"""
\t\t--- affine --
Original:
========
{clear_message}
Noisy message:
=============
{noisy_message}
Noisy Encrypted:
===============
{encr_noisy}
Noisy Decrypted:
===============
{decr_noisy}
Cleaned Decrypted:
=================
{decr_without_noise}
"""
)
--- affine -- Original: ======== This is a clear message to encrypt and decrypt. I add this to make it longer. Noisy message: ============= T<hHi.sb 3i!s5 waw Zc9l|esaVrk Km+eHs$sQaKg e, Vt_o" 5e7nCc5rQylpRtA na2nxdL TdkeacfryyYp]t'.b 7I Uacd<dH otRhZi,sW ut?o6 1m]a;kEe_ &i$t4 BlhoanWg`e r'.Z Noisy Encrypted: =============== = g^LuaGkOLPazkVbVk],o\0Wabh|wknAeW^a`a-bn!@WJkhF7l5kzWD&#,z|- \QsFYk&bj&;rSk=rwWb,<| xQmFpuGkDC@k"b,r r^klFsg]LJaMk+F.l_k$Amb9wNW7k*L`F4k>\glb&M!}W@|pu] Noisy Decrypted: =============== T<hHi.sb 3i!s5 waw Zc9l|esaVrk Km+eHs$sQaKg e, Vt_o" 5e7nCc5rQylpRtA na2nxdL TdkeacfryyYp]t'.b 7I Uacd<dH otRhZi,sW ut?o6 1m]a;kEe_ &i$t4 BlhoanWg`e r'.Z Cleaned Decrypted: ================= This is a clear message to encrypt and decrypt. I add this to make it longer.
encr_noisy = perm_key.encrypt(noisy_message)
decr_noisy = perm_key.decrypt(encr_noisy)
decr_without_noise = utils.remove_noises(decr_noisy)
print(
f"""
\t\t--- PERMUTATION ---
Original:
========
{clear_message}
Noisy message:
=============
{noisy_message}
Noisy Encrypted:
===============
{encr_noisy}
Noisy Decrypted:
===============
{decr_noisy}
Cleaned Decrypted:
=================
{decr_without_noise}
"""
)
--- PERMUTATION --- Original: ======== This is a clear message to encrypt and decrypt. I add this to make it longer. Noisy message: ============= T<hHi.sb 3i!s5 waw Zc9l|esaVrk Km+eHs$sQaKg e, Vt_o" 5e7nCc5rQylpRtA na2nxdL TdkeacfryyYp]t'.b 7I Uacd<dH otRhZi,sW ut?o6 1m]a;kEe_ &i$t4 BlhoanWg`e r'.Z Noisy Encrypted: =============== <hTi.Hb si!35 sawwZc l|9saerkVKm eH+$ssaKQ eg V,_ot 5"7nec5CQyrpRlA ta2nxdn TLkedcfayyrp]Y'.t 7b IacU<dd oHRhti,ZW st?u6 om]1;kae_E&i t4$Bl oahWgne `'.rZ Noisy Decrypted: =============== T<hHi.sb 3i!s5 waw Zc9l|esaVrk Km+eHs$sQaKg e, Vt_o" 5e7nCc5rQylpRtA na2nxdL TdkeacfryyYp]t'.b 7I Uacd<dH otRhZi,sW ut?o6 1m]a;kEe_ &i$t4 BlhoanWg`e r'.Z Cleaned Decrypted: ================= This is a clear message to encrypt and decrypt. I add this to make it longer.
Asymmetric: Public-key cryptography¶
Knowing $f_\theta$ or $\theta\in \Theta$ (thus $f_\theta$) does NOT make the construction of $\left(f_\theta\right)^{-1}$ (thus $\theta^\prime$) easy.
From Wikipedia (2023-05-01):
Public-key cryptography, or asymmetric cryptography, is the field of cryptographic systems that use pairs of related keys. Each key pair consists of a public key and a corresponding private key. Key pairs are generated with cryptographic algorithms based on mathematical problems termed one-way functions. Security of public-key cryptography depends on keeping the private key secret; the public key can be openly distributed without compromising security.
Rivest–Shamir–Adleman aka RSA¶
$\Theta \approx \mathbb{N} \times \mathbb{N}$
public key: $\theta = (n,d)$
private key: $\theta^\prime = (n,e)$
function: $f_{(n,a)}: x\mod n \mapsto x^a \mod n$
Construction:
- Generate two primes $p,q$.
- Compute $n=p\times q$.
- Compute $\varphi(n) = (p-1)*(q-1)$.
- Find $d$ such that $\gcd\left( d, \varphi(n)\right) = 1$.
- Find $e$ such that $de + u\varphi(n)=1$ for some $u\in \mathbb{Z}$.
- Discard $p,q,\varphi(n)$ and $u$ (for security).
Note: [4.] and [5.] can be done easily and at once with the extended Euclidean algorithm.
:warning: Security: number factorization complexity
For large composite number $n$, computing $\varphi(n)$ is not easy without knowing its prime factors.
rsa_key = crypto.RSAPairKeys.generate_pairs(16)
print(rsa_key)
RSAPairKeys((31609, 22171), HIDDEN_KEY)
rsa_pub, rsa_priv = rsa_key
rsa_pub, rsa_priv
((31609, 22171), (31609, 6163))
rsa_pub, rsa_priv = crypto.RSAPairKeys.generate_pairs(bit_size = 16, use_d_17 = True)
rsa_pub, rsa_priv
((29321, 17), (29321, 8513))
encr_rsa = rsa_key.encrypt(clear_message)
encr_rsa
'4n83#*^O2pe_|X~-E;T92[I=A[u9g2DpVs;vd^D)%:&@g1/u0n6xe%_21LN6;w=6S0Hn|cgA"9Y`d'
decr_rsa = rsa_key.decrypt(encr_rsa)
decr_rsa
'This is a clear message to encrypt and decrypt. I add this to make it longer.'
Diffie–Hellman key exchange¶
- The network will:
- choose a shared group $(G,*)$,
- an element $g\in G$,
- and compute $ord(g) \in \mathbb{N}$ for which is the smallest where $ord(g)\cdot g := \underbrace{g*g*\cdots*g}_{ord(g) \text{ times}}$ is equal to the neutral element of $G$.
- For each $i$ in the network:
- Choose $\theta^\prime_i \in \mathbb{N}$ such that $\theta^\prime_i < ord(g)$.
- Compute $f_{\theta_i} = \theta^\prime_i \cdot g$.
- Publish $f_{\theta_i}$ and keep $\theta^\prime_i$ secret.
If $i$ wants to send a message $m$ to $j$, he will proceed as follows:
- retrieve $f_{\theta_j}$,
- compute $P_{ij} := \theta_i \cdot f_{\theta_j}$,
- encrypt $m$ with $P_{ij}$ (like the symmetric fashion),
- send encrypted message $m_{ij}$ to $j$.
When $j$ receives $m_{ij}$, he will:
- retrieve $f_{\theta_i}$,
- compute $P_{ji} := \theta_j \cdot f_{\theta_i}$,
- decrypt $m_{ij}$ with $P_{ji}$.
Note: $$ \begin{align*} P_{ij} &= \theta_i \cdot f_{\theta_j}&\\ &= \underbrace{f_{\theta_j}*\cdots*f_{\theta_j}}_{\theta_i \text{ times}}&\\ &= \underbrace{\left(\underbrace{g*\cdots*g}_{\theta_j \text{ times}}\right)*\cdots*\left(\underbrace{g*\cdots*g}_{\theta_j \text{ times}}\right)}_{\theta_i \text{ times}}&\\ &= \underbrace{g*\cdots*g}_{\theta_i\theta_j \text{ times}}&\\ &= \underbrace{\left(\underbrace{g*\cdots*g}_{\theta_j \text{ times}}\right)*\cdots*\left(\underbrace{g*\cdots*g}_{\theta_i \text{ times}}\right)}_{\theta_j \text{ times}}&\\ &= \underbrace{f_{\theta_j}*\cdots*f_{\theta_i}}_{\theta_j \text{ times}}&\\ &= \theta_j \cdot f_{\theta_i}&\\ &= P_{ji}& \end{align*}$$
⚠️ Security: discrete logarithm
For some group (Diffie-Hellman Group), knowing the value of $g$ and $n \cdot g$ does not lead to $n$ easily.
# coming soon
Elliptic Curve Cryptosystem¶
ECC use the DH key exchange with Where the group $G$ is an elliptic curve over finite fields.
# coming soon
Communication & Security¶
Nodes generation¶
from bcbcpy.node import Node
key = crypto.RSAPairKeys.generate_pairs()
node = Node(key)
node.id
'user_785253ae'
node
<bcbcpy.node.Node at 0x1068ff760>
class RepNode(Node):
def __repr__(self) -> str:
return f"{self.id} : {self._Node__keys}" # type:ignore
akey = crypto.RSAPairKeys.generate_pairs()
alice = RepNode(akey, "Alice")
alice
Alice_0e2482b2 : RSAPairKeys((26909, 6767), HIDDEN_KEY)
bkey = crypto.RSAPairKeys.generate_pairs()
bob = RepNode(username="Bob", keys=bkey)
bob
Bob_f2980b85 : RSAPairKeys((30847, 12877), HIDDEN_KEY)
rkey = crypto.RSAPairKeys.generate_pairs()
random_guy = RepNode(rkey)
random_guy
user_f4cc4c04 : RSAPairKeys((32447, 15721), HIDDEN_KEY)
Communication¶
sender = alice
receiver = bob
plain_message = (
"Hello Bob! This is Alice. Please send me $1000 to this account: (1234567890)"
)
cipher = sender.encrypt(plain_message, key=receiver.pub)
cipher
'/(\\I$f*M6w.lP?hux\n~"S,Nq~u,a:Ge}]\\`pgHr\na^vX#zB\t&x/K0/5QB~?0WZ|kP|1&r5@v*Hf`$'
receiver.decrypt(cipher)
'Hello Bob! This is Alice. Please send me $1000 to this account: (1234567890)'
Attack¶
Reading attack¶
hacker = random_guy
hacker.decrypt(cipher)
'/(x>-.{^hQmy8iO&N8kI/#`\nn~z\\Zd3K:-uD9WGQ!MGzv=X1?C{8p@@jLN[oZyVW5mZgu$s,lyjY4'
hacker.encrypt(cipher, receiver.pub)
'@Au:rqlpys G\\bqy=p7~U&v0\tLpt&DvnPD%wN9l<03"5$94e/Jsozcp= Bl|\\_L%r$7}vo:<?SFa-'
Q: what if receiver.pub == receiver.priv
?¶
_, receiver_priv = receiver._Node__keys
receiver_pub = receiver_priv # fake pub for testing
hacker.encrypt(cipher, receiver_pub)
'Hello Bob! This is Alice. Please send me $1000 to this account: (1234567890)'
Sending attack¶
scam_message = (
"Hello Bob! This is Alice. Please send me $1000 to this account: (0987654321)"
)
scam_cipher= hacker.encrypt(scam_message, key=receiver.pub)
scam_cipher
'/(\\I$f*M6w.lP?hux\n~"S,Nq~u,a:Ge}]\\`pgHr\na^vX#zB\t&x/K0/5QB~?0WZ|kP|Vl"Ru|:ZAfh'
receiver.decrypt(scam_cipher)
'Hello Bob! This is Alice. Please send me $1000 to this account: (0987654321)'
Digital Signature¶
Problem: no way for the receiver
to recognize the true
sender
.
Authentication¶
Encryption¶
sender.sign(plain_message) == sender.decrypt(plain_message) # encrypt with sender.priv
True
signed_message = sender.sign(plain_message)
cipher_signed = sender.encrypt(signed_message, key=receiver.pub)
cipher_signed
"%%KmF*,/v.;%\t!D+p`UKMFA#PrAvG;L\n[YRgq'zN7cw)v\\`dx+DuK*V,{e<>I6VgSL\\Tf*.U$X%Pb"
with_noise = utils.add_noises(plain_message)
signed_with_noise = sender.sign(with_noise)
cipher_signed_with_noise = sender.encrypt(signed_with_noise, key=receiver.pub)
cipher_signed_with_noise
'#,U5ZY]vsj?!xQr&2Y,gt\tC\t(Jv[rdGF$mOu_Rt\nJ)ciC\\\'I3E0)1V`Z=/HqQmmnVZLu.4}]k40Zcm^@6=8.mV^(] }V`jqlI4`W|s`\\X9>}#3"~C$["V[dv;eOM9x"I *rc Q3`C\t6{VGE?juife)g0T8'
Shortcut¶
cipher_signed == sender.sends(plain_message, _to=receiver.pub)
True
cipher_signed == sender.sends(plain_message, _to=receiver)
True
cipher_signed_with_noise_bis = sender.sends(plain_message, _to=receiver, _with_noises=True)
cipher_signed_with_noise_bis
'NoIsYnOiSy&oQr-*k$\tO\n}V-gU2\\]2RfWng4yGB7DH^gigU*^,5S;w\\\\:<9YS\'wSGk# ^:TnyPM1+8YpK2kF[B}\'K|iW+I\nofR>a2SOvA[;kCV|FU"l}xa_`=*X5`^|=(0j#o@J5"V5c[`$8;m~f</dlmtWT#w0&q|)2NoIsYnOiSy'
cipher_signed_with_noise_bis[10:-10] # trim NoIsYnOiSy
'&oQr-*k$\tO\n}V-gU2\\]2RfWng4yGB7DH^gigU*^,5S;w\\\\:<9YS\'wSGk# ^:TnyPM1+8YpK2kF[B}\'K|iW+I\nofR>a2SOvA[;kCV|FU"l}xa_`=*X5`^|=(0j#o@J5"V5c[`$8;m~f</dlmtWT#w0&q|)2'
cipher_signed_with_noise == cipher_signed_with_noise_bis[10:-10]
False
Q: Do you know why is it False
?¶
Decryption¶
signed_message = receiver.decrypt(cipher_signed)
receiver.encrypt(signed_message, key=sender.pub) # remove signature
'Hello Bob! This is Alice. Please send me $1000 to this account: (1234567890)'
signed_message_with_noise = receiver.decrypt(cipher_signed_with_noise)
message_with_noise = receiver.encrypt(signed_message_with_noise, key=sender.pub) # unsign
utils.remove_noises(message_with_noise)
'Hello Bob! This is Alice. Please send me $1000 to this account: (1234567890)'
trimmed_cipher_signed_with_noise = cipher_signed_with_noise_bis[10:-10]
signed_message_with_noise = receiver.decrypt(trimmed_cipher_signed_with_noise)
message_with_noise = receiver.encrypt(signed_message_with_noise, key=sender.pub) # unsign
utils.remove_noises(message_with_noise)
'Hello Bob! This is Alice. Please send me $1000 to this account: (1234567890)'
Shortcut¶
receiver.gets(cipher_signed, _from=sender.pub)
'Hello Bob! This is Alice. Please send me $1000 to this account: (1234567890)'
utils.remove_noises(receiver.gets(cipher_signed_with_noise, _from=sender))
'Hello Bob! This is Alice. Please send me $1000 to this account: (1234567890)'
receiver.gets(cipher_signed_with_noise_bis, _from=sender)
'Hello Bob! This is Alice. Please send me $1000 to this account: (1234567890)'
Attacks¶
Reading Attack¶
hacker.decrypt(cipher_signed)
'\'QtRrx_TVbhk*gB;k`iZgfw\'OO|V;.qU10\'8wdqSi+DHf\tk";k,c+9\'bO{p*?;2a3nIF/=<= ZOtt'
hacker.encrypt(cipher_signed, key=sender.pub)
'\n*2PD\\72Nk-<mFi?]0e(q2VsypDR.xV\n-bpXie;<u$C<Mx$e\\\toXN\t()T5g4UIl|W!e?b"%}%)Er+\'I'
hacker.encrypt(cipher_signed, key=receiver.pub)
'B7yTZ_=Ga+ yWJv=_\'N3JVF+e&ou;h~@/[s PL[K;$]zfP"\\ET/%*^*Tf5NlFl*p`ujEN[2b&iqT='
hacker.sends(cipher_signed, _to=receiver.pub)
'C22"W7(U+&O]Un@,j5qHcaSXzA4%n=1rgr3Pyl$unaD=h"3\n}a[XX3Y!,kHgCCGU4*TnQF^c|e}W*'
hacker.gets(cipher_signed, _from=sender)
'y6Ou`U~5P+*9;9O!Di!"Biy-CpZ\niT=wc {OUgoNQQ@1/sg E\npP3|\nW>K]qEnrT]\'WXp/;`\tz97u'
hacker.gets(cipher_signed, _from=bob)
'C22"W7(U+&O]Un@,j5qHcaSXzA4%n=1rgr3Pyl$unaD=h"3\n}a[XX3Y!,kHgCCGU4*TnQF^c|e}W*'
Sending Attack¶
print(f"""
Original:
========
{plain_message}
Scam:
=====
{scam_message}
"""
)
Original: ======== Hello Bob! This is Alice. Please send me $1000 to this account: (1234567890) Scam: ===== Hello Bob! This is Alice. Please send me $1000 to this account: (0987654321)
non_signed_scam = hacker.encrypt(scam_message, key=receiver.pub)
receiver.gets(non_signed_scam, _from=sender)
'TM4K:{1*yNvfc9an6],hF\t{q?Pm4\tD8\tJi\\K/I]\n#$vY#6Z\\Z=n}<ft@1F\n\\\tK4+?y2^i$&\nP8(:'
non_signed_scam = hacker.encrypt(scam_message, key=sender.pub)
receiver.gets(non_signed_scam, _from=sender)
"Md-q\nTbhluxC)E|4?X`D^W.t3`\\9r(| chR3;V&5uL.(]Lpd'fwN]\\GG#M3Ou^nIduJG\\@|!2E}l-C"
signed_scam = hacker.sends(scam_message, _to=receiver)
receiver.gets(signed_scam, _from=sender)
'jiyoDsBL{@>V W1S_IcBuWBC(`P,*<lwu..fYxlHj4w_bJmt3*eBJLrFW\\quvB!{Y\t3c]Lm\\.`)zsf'
Possible Weakness¶
_, leaked_sender_priv = sender._Node__keys # key of alice = expected sender
fake_signed_scam = hacker.encrypt(scam_message, leaked_sender_priv)
cipher_fake_signed_scam = hacker.encrypt(fake_signed_scam, receiver.pub)
cipher_fake_signed_scam
"%%KmF*,/v.;%\t!D+p`UKMFA#PrAvG;L\n[YRgq'zN7cw)v\\`dx+DuK*V,{e<>I6VgSL=[#Zx>5=A~`"
receiver.gets(cipher_fake_signed_scam, _from=sender)
'Hello Bob! This is Alice. Please send me $1000 to this account: (0987654321)'
Digital signature¶
ElGamal¶
Blockchain¶
from bcbcpy import blockchain
Recap: Data Structure¶
List - Array¶
Linked List¶
Block¶
from bcbcpy.blockchain.block import InitialBlock, Block
Initial Block¶
init_block = InitialBlock(initial_data="initial data")
init_block.data
'initial data'
init_block.hash
'00002d64bd7129caa2aa8ec458081519ac26034158e60962f6fd1428e22b1036'
init_block
{ "prev_hash": "", "hash": "00002d64bd7129caa2aa8ec458081519ac26034158e60962f6fd1428e22b1036", "data": "initial data", "nonce": 26206 }
Block Data¶
second_block = Block(data="second data", prev_block= init_block)
second_block
{ "prev_hash": "00002d64bd7129caa2aa8ec458081519ac26034158e60962f6fd1428e22b1036", "hash": "ac19b9c98cc9a1655bd66662c552b8667c2140a451ce635b24aa2fdf34555aea", "data": "second data", "nonce": 0 }
second_block.is_valid()
False
second_block.mine()
second_block
{ "prev_hash": "00002d64bd7129caa2aa8ec458081519ac26034158e60962f6fd1428e22b1036", "hash": "0000f1a7cc9b3335858fdc615b274e07568931de65e82fb991ac257e168b89c1", "data": "second data", "nonce": 12005 }
second_block.is_valid()
True
third_block = Block("third data", prev_block= second_block)
third_block
{ "prev_hash": "0000f1a7cc9b3335858fdc615b274e07568931de65e82fb991ac257e168b89c1", "hash": "f94643bbf3edffb31b549ce63a5adbd4542b7e3b6d45f32ae69da4f9bc87b6f7", "data": "third data", "nonce": 0 }
third_block.mine()
third_block
{ "prev_hash": "0000f1a7cc9b3335858fdc615b274e07568931de65e82fb991ac257e168b89c1", "hash": "0000eecdfce497ff979154b54ddb7f8fe0114683c0102afb900cd93497951e80", "data": "third data", "nonce": 2917 }
forth_block = Block("forth data", third_block)
forth_block.mine()
forth_block
{ "prev_hash": "0000eecdfce497ff979154b54ddb7f8fe0114683c0102afb900cd93497951e80", "hash": "00003e75b774fcf431090692c595e06fa5d05bc7730a63673e4e4304eb4faf6a", "data": "forth data", "nonce": 101056 }
fifth_block = Block("fifth block", forth_block)
fifth_block.mine()
fifth_block
{ "prev_hash": "00003e75b774fcf431090692c595e06fa5d05bc7730a63673e4e4304eb4faf6a", "hash": "0000d8cb89717a6b1904bf4f44aab11529b3ee378d0256620f608c3dd73694b9", "data": "fifth block", "nonce": 34705 }
init_block.is_valid(), second_block.is_valid(), third_block.is_valid(), forth_block.is_valid(), fifth_block.is_valid()
(True, True, True, True, True)
Making some change in past data¶
third_block.data = "not third data"
third_block
{ "prev_hash": "0000f1a7cc9b3335858fdc615b274e07568931de65e82fb991ac257e168b89c1", "hash": "66ad1a8ee995ddbcaaf131585222c10a396a960f0c441578d5613417fa5d107a", "data": "not third data", "nonce": 2917 }
init_block.is_valid(), second_block.is_valid(), third_block.is_valid(), forth_block.is_valid(), fifth_block.is_valid()
(True, True, False, False, False)
third_block.data = "third data"
init_block.is_valid(), second_block.is_valid(), third_block.is_valid(), forth_block.is_valid(), fifth_block.is_valid()
(True, True, True, True, True)
third_block.data = "not third data"
third_block.mine()
third_block.is_valid()
True
init_block.is_valid(), second_block.is_valid(), third_block.is_valid(), forth_block.is_valid(), fifth_block.is_valid()
(True, True, True, False, False)
third_block.data = "third data"
third_block.mine()
init_block.is_valid(), second_block.is_valid(), third_block.is_valid(), forth_block.is_valid(), fifth_block.is_valid()
(True, True, True, True, True)
Blockchain¶
A submodule is available at
from bcbcpy.blockchain import chain
Root¶
blockchain.RootChain()
{ "root_block": { "hash": "0000a456e7b5a5eb059e721fb431436883143101275c4077f83fe70298f5623d", "data": "" } }
initial_data = {"INITIAL AGREEMENT": {"Bob": 1000, "Alice": 500}}
initial_data_string = utils.obj2txt(initial_data, indent=4)
root_chain = blockchain.RootChain(initial_data_string, difficulty=4)
root_chain
{ "root_block": { "hash": "0000579da420f2b62497716cc60e95a0f7ad46336c42062b7485a27fd91d99f0", "data": "{\n \"INITIAL ..." } }
Chain¶
prev_block = root_chain.last_block
new_block = Block("new block", prev_block)
new_block
{ "prev_hash": "0000579da420f2b62497716cc60e95a0f7ad46336c42062b7485a27fd91d99f0", "hash": "73095d20a662a26d26eec210c0dc51c34816098cdab05f941487ee5e8133d6ad", "data": "new block", "nonce": 0 }
root_chain.add_block(
new_block
)
--------------------------------------------------------------------------- AssertionError Traceback (most recent call last) /var/folders/x4/45r2t3bx5l3dc_b3tm_yz7380000gn/T/ipykernel_76208/2453956519.py in <cell line: 1>() ----> 1 root_chain.add_block( 2 new_block 3 ) ~/Git/aheritianad/BootCamp-BlockChain-and-Python/bcbcpy/blockchain/chain.py in add_block(self, block) 49 self.last_block is block.prev_block 50 ), "Incompatible block to the head block of the chain." ---> 51 assert block.is_valid(), "Block is not valid." 52 self.last_block = block 53 self._length += 1 AssertionError: Block is not valid.
new_block.mine() # validation
new_block
{ "prev_hash": "0000579da420f2b62497716cc60e95a0f7ad46336c42062b7485a27fd91d99f0", "hash": "00007b581d241fea3fb8bf4c7c5c1941af9abb3a5ce05abb97b4521d34bab727", "data": "new block", "nonce": 2823 }
root_chain.add_block(
new_block
)
root_chain
{ "root_block": { "hash": "0000579da420f2b62497716cc60e95a0f7ad46336c42062b7485a27fd91d99f0", "data": "{\n \"INITIAL ..." }, "last_block": { "hash": "00007b581d241fea3fb8bf4c7c5c1941af9abb3a5ce05abb97b4521d34bab727", "data": "new block" } }
chain = blockchain.Chain(root_chain=root_chain, info="local network")
chain
{ "root_block": { "hash": "000015420debcdd55c00babc9c3a5a9c4ab22280cf9a6874986c427281dd79b9", "data": "local network" } }
Transaction¶
from bcbcpy.blockchain.block import transaction
Nodes setup¶
from bcbcpy.blockchain.node import Node
alice = Node(crypto.RSAPairKeys.generate_pairs(), "Alice")
bob = Node(crypto.RSAPairKeys.generate_pairs(), "Bob")
other_person_in_the_network = Node(crypto.RSAPairKeys.generate_pairs())
Transaction Data¶
assets = 1000
sender = bob
receiver = alice
receiver_id = receiver.id
receiver_pub = receiver.pub
prev_block = chain.last_block
transaction_data = transaction.TransactionData(
assets, sender, receiver_id, receiver_pub, prev_block
)
print(transaction_data)
{'timestamp': '2023-06-02T19:11:45.766410', 'sender_id': 'Bob_4a985b38', 'receiver_id': 'Alice_a9f23756', 'assets': 1000, 'prev_hash': '000015420debcdd55c00babc9c3a5a9c4ab22280cf9a6874986c427281dd79b9', 'sender_confirmation': 'N.ieM2R8OZN)+8#{AsTa/@6K*43*Y;/Ra#;{Jmo#E\t_!gW|m>!w!nYa7t5k.-*djh]9SzbaCyL+C2ga*wTn!4Ab378[I_&Q36tA6Zi$PAaZ]&.6nu3x&Co2}APU-S>4c2|N\tObl'}
Transaction Block¶
transaction_block = blockchain.TransactionBlock(transaction_data)
transaction_block
{ "prev_hash": "000015420debcdd55c00babc9c3a5a9c4ab22280cf9a6874986c427281dd79b9", "hash": "a5e3c7badc73a309ceb57ff88320534a1dc1a06abf475545d85adc46942e96ad", "data": "{'timestamp': '2023-06-02T19:11:45.766410', 'sender_id': 'Bob_4a985b38', 'receiver_id': 'Alice_a9f23756', 'assets': 1000, 'prev_hash': '000015420debcdd55c00babc9c3a5a9c4ab22280cf9a6874986c427281dd79b9', 'sender_confirmation': 'N.ieM2R8OZN)+8#{AsTa/@6K*43*Y;/Ra#;{Jmo#E\\t_!gW|m>!w!nYa7t5k.-*djh]9SzbaCyL+C2ga*wTn!4Ab378[I_&Q36tA6Zi$PAaZ]&.6nu3x&Co2}APU-S>4c2|N\\tObl'}", "nonce": 0 }
Verification¶
Anyone in the network¶
data = transaction_block.data
print(data)
{'timestamp': '2023-06-02T19:11:45.766410', 'sender_id': 'Bob_4a985b38', 'receiver_id': 'Alice_a9f23756', 'assets': 1000, 'prev_hash': '000015420debcdd55c00babc9c3a5a9c4ab22280cf9a6874986c427281dd79b9', 'sender_confirmation': 'N.ieM2R8OZN)+8#{AsTa/@6K*43*Y;/Ra#;{Jmo#E\t_!gW|m>!w!nYa7t5k.-*djh]9SzbaCyL+C2ga*wTn!4Ab378[I_&Q36tA6Zi$PAaZ]&.6nu3x&Co2}APU-S>4c2|N\tObl'}
sender_confirmation="""N.ieM2R8OZN)+8#{AsTa/@6K*43*Y;/Ra#;{Jmo#E\t_!gW|m>!w!nYa7t5k.-*djh]9SzbaCyL+C2ga*wTn!4Ab378[I_&Q36tA6Zi$PAaZ]&.6nu3x&Co2}APU-S>4c2|N\tObl"""
print(sender_confirmation)
N.ieM2R8OZN)+8#{AsTa/@6K*43*Y;/Ra#;{Jmo#E _!gW|m>!w!nYa7t5k.-*djh]9SzbaCyL+C2ga*wTn!4Ab378[I_&Q36tA6Zi$PAaZ]&.6nu3x&Co2}APU-S>4c2|N Obl
confirmation_string = other_person_in_the_network.encrypt(sender_confirmation, key= sender.pub)
print(confirmation_string)
[['assets', 1000], ['receiver_pub', (44069, 19667)], ['prev_hash', 000015420debcdd55c00babc9c3a5a9c4ab22280cf9a6874986c427281dd79b9]]
print(receiver.pub)
print(chain.hash)
(44069, 19667) 000015420debcdd55c00babc9c3a5a9c4ab22280cf9a6874986c427281dd79b9
Block Validation¶
transaction_block.is_valid()
False
transaction_block.mine()
transaction_block
{ "prev_hash": "000015420debcdd55c00babc9c3a5a9c4ab22280cf9a6874986c427281dd79b9", "hash": "0000561773ecab4888f4d9ffa759525f5a9ee00073962be29c00330e2d5356fd", "data": "{'timestamp': '2023-06-02T19:11:45.766410', 'sender_id': 'Bob_4a985b38', 'receiver_id': 'Alice_a9f23756', 'assets': 1000, 'prev_hash': '000015420debcdd55c00babc9c3a5a9c4ab22280cf9a6874986c427281dd79b9', 'sender_confirmation': 'N.ieM2R8OZN)+8#{AsTa/@6K*43*Y;/Ra#;{Jmo#E\\t_!gW|m>!w!nYa7t5k.-*djh]9SzbaCyL+C2ga*wTn!4Ab378[I_&Q36tA6Zi$PAaZ]&.6nu3x&Co2}APU-S>4c2|N\\tObl'}", "nonce": 16752 }
transaction_block.is_valid()
True
Adding to Chain¶
chain.add_block(transaction_block)
chain
{ "root_block": { "hash": "000015420debcdd55c00babc9c3a5a9c4ab22280cf9a6874986c427281dd79b9", "data": "local network" }, "last_block": { "hash": "0000561773ecab4888f4d9ffa759525f5a9ee00073962be29c00330e2d5356fd", "data": "{'timestamp': '..." } }
Second Transaction¶
assets = 500
sender = alice
receiver = bob
prev_block = chain.last_block
transaction_block = sender.make_transaction_block(
assets=assets,
to=receiver,
prev_block=prev_block,
)
Verification ...¶
data = transaction_block.data
print(data)
{'timestamp': '2023-06-02T19:12:50.485053', 'sender_id': 'Alice_a9f23756', 'receiver_id': 'Bob_4a985b38', 'assets': 500, 'prev_hash': '0000561773ecab4888f4d9ffa759525f5a9ee00073962be29c00330e2d5356fd', 'sender_confirmation': "\n(o@?U=<=K8d+4)X8Wl*`c6lF!9y2U==m=AWPa,BZPKO2{`MKYQG\\wFr(}G\n@Ewk_<')VZyIj-)9@c}9$6_v%?5s%iS9}Qg#1<y20yFuL5\tGl0IXA@Z)S8g[\tR<T?T{\\0H\n.Lf"}
sender_confirmation="""\n(o@?U=<=K8d+4)X8Wl*`c6lF!9y2U==m=AWPa,BZPKO2{`MKYQG\\wFr(}G\n@Ewk_<')VZyIj-)9@c}9$6_v%?5s%iS9}Qg#1<y20yFuL5\tGl0IXA@Z)S8g[\tR<T?T{\\0H\n.Lf"""
confirmation = other_person_in_the_network.encrypt(sender_confirmation, key= sender.pub)
print(confirmation)
[['assets', 500], ['receiver_pub', (60833, 30109)], ['prev_hash', 0000561773ecab4888f4d9ffa759525f5a9ee00073962be29c00330e2d5356fd]]
print(receiver.pub)
print(chain.hash)
(60833, 30109) 0000e6ecd7240be8403b29af870cf26480d6a50e0d1ae05c295729a1b6e639cf
Adding to Chain¶
transaction_block.mine() # validation
chain.add_block(transaction_block)
--------------------------------------------------------------------------- AssertionError Traceback (most recent call last) /var/folders/x4/45r2t3bx5l3dc_b3tm_yz7380000gn/T/ipykernel_76208/4277047408.py in <cell line: 2>() 1 transaction_block.mine() # validation ----> 2 chain.add_block(transaction_block) ~/Git/aheritianad/BootCamp-BlockChain-and-Python/bcbcpy/blockchain/chain.py in add_block(self, block) 47 def add_block(self, block: Block) -> None: 48 assert ( ---> 49 self.last_block is block.prev_block 50 ), "Incompatible block to the head block of the chain." 51 assert block.is_valid(), "Block is not valid." AssertionError: Incompatible block to the head block of the chain.
chain
{ "root_block": { "hash": "000015420debcdd55c00babc9c3a5a9c4ab22280cf9a6874986c427281dd79b9", "data": "local network" }, "...": "...", "last_block": { "hash": "0000e6ecd7240be8403b29af870cf26480d6a50e0d1ae05c295729a1b6e639cf", "data": "{'timestamp': '..." } }