As criptomoedas, como o Bitcoin, utilizam-se de tecnologias criptográficas como criptografia de chave publica,e funções de Hash. Neste notebook vamos nos familiarizar com estes conceitos que nos serão úteis em nosso estudo da bitcoin e outras criptomoedas.
As funções de Hash criptográfico são o componentes mais fundamental da maioria das blockchains pois é a "cola" que garante a coesão, correção, imutabilidade e outras características fundamentais das blockchains.
Uma função de Hash é uma função que apresenta algumas características básicas:
A biblioteca padrão do Python nos oferece uma biblioteca com implementações das principais funções de hash, a Hashlib.
In [2]:
import hashlib
hashlib.algorithms_available
Out[2]:
A Bitcoin se utiliza de curvas elípticas para suas necessidades criptográficas. Mais precisamente, utiliza o algoritmo de assinatura digital por curvas elipticas (ECDSA). A ECDSA envolve três componentes principais: uma chave pública, uma chave privada e assinatura.
A Bitcoin usa uma curva elíptica específica chamada secp256k1. A função em si parece inofensiva: $$y^2=x^3+7$$ onde $4a^3 +27b^2 \neq 0$ (para excluir curvas singulares). $$\begin{array}{rcl} \left\{(x, y) \in \mathbb{R}^2 \right. & \left. | \right. & \left. y^2 = x^3 + ax + b, \right. \\ & & \left. 4a^3 + 27b^2 \ne 0\right\}\ \cup\ \left\{0\right\} \end{array}$$
Porém, em aplicações criptográficas, esta função não é definida sobre os números reais, mas sobre um campo de números primos: mais precisamente ${\cal Z}$ modulo $2^{256} - 2^{32} - 977$.
\begin{array}{rcl} \left\{(x, y) \in (\mathbb{F}_p)^2 \right. & \left. | \right. & \left. y^2 \equiv x^3 + ax + b \pmod{p}, \right. \\ & & \left. 4a^3 + 27b^2 \not\equiv 0 \pmod{p}\right\}\ \cup\ \left\{0\right\} \end{array}Para um maior aprofundamento sobre a utilização de curvas elítpicas em criptografia leia este material.
A forma mais simples de criptografia é a criptografia simétrica, na qual se utilizando de uma chave gerada aleatóriamente, converte um texto puro em um texto encriptado. então de posse da mesma chave é possível inverter a operação, recuperando o texto original. Quando falamos em texto aqui estamos falando apenas de uma aplicação possível de criptografia. Na verdade o que será aplicado aqui para textos, pode ser aplicado para qualquer sequencia de bytes, ou seja para qualquer objeto digital.
In [6]:
from Crypto.Cipher import DES3
from Crypto import Random
Neste exemplo vamos usar o algoritmo conhecido como "triplo DES" para encriptar e desencriptar um texto. Para este exemplo a chave deve ter um comprimento múltiplo de 8 bytes.
In [7]:
chave = b"chave secreta um"
sal = Random.get_random_bytes(8)
des3 = DES3.new(chave, DES3.MODE_CFB, sal)
Note que adicionamos sal à ao nosso encriptador. o "sal" é uma sequência aleatória de bytes feitar para dificultar ataques.
In [9]:
texto = b"Este e um texto super secreto que precisa ser protegido a qualquer custo de olhares nao autorizados."
enc = des3.encrypt(texto)
enc
Out[9]:
In [12]:
des3 = DES3.new(chave, DES3.MODE_CFB, sal)
des3.decrypt(enc)
Out[12]:
Um dos problemas com esta metodologia de encriptação, é que se você deseja enviar este arquivo encriptado a um amigo, terá que encontrar uma forma segura de lhe transmitir a chave, caso contrário um inimigo mal intencionado poderá desencriptar sua mensagem de posse da chave. Para resolver este problema introduzimos um novo métodos de encriptação:
Nesta metodologia temos duas chaves: uma pública e outra privada.
In [7]:
from Crypto.PublicKey import RSA
from Crypto.Random import get_random_bytes
from Crypto.Cipher import AES, PKCS1_OAEP
Vamos criar uma chave privada, e também encriptá-la, no caso de termos que mantê-la em algum lugar onde possa ser observada por um terceiro.
In [10]:
senha = "minha senha super secreta."
key = RSA.generate(2048) # Chave privada
print(key.exportKey())
chave_privada_encryptada = key.exportKey(passphrase=senha, pkcs=8, protection="scryptAndAES128-CBC")
In [17]:
publica = key.publickey()
publica.exportKey()
Out[17]:
De posse da senha podemos recuperar as duas chaves.
In [18]:
key2 = RSA.import_key(chave_privada_encryptada, passphrase=senha)
print(key2==key)
key.publickey().exportKey() == key2.publickey().exportKey()
Out[18]:
Agora podemos encriptar algum documento qualquer. Para máxima segurança, vamos usar o protocolo PKCS#1 OAEP com a algoritmo RSA para encriptar assimetricamente uma chave de sessão AES. Esta chave de sessão pode ser usada para encriptar os dados. Vamos usar o modo EAX para permitir a detecção de modificações não autorizadas.
In [31]:
data = "Minha senha do banco é 123456".encode('utf8')
chave_de_sessão = get_random_bytes(16)
# Encripta a chave de sessão com a a chave RSA pública.
cifra_rsa = PKCS1_OAEP.new(publica)
chave_de_sessão_enc = cifra_rsa.encrypt(chave_de_sessão)
# Encrypta os dados.
cifra_aes = AES.new(chave_de_sessão, AES.MODE_EAX)
texto_cifrado, tag = cifra_aes.encrypt_and_digest(data)
O destinatário da mensagem pode então desencriptar a mensagem usando a chave privada para desencriptar a chave da sessão, e com esta a mensagem.
In [32]:
# Desencripta a chave de sessão com a chave privada RSA.
cifra_rsa = PKCS1_OAEP.new(key)
chave_de_sessão = cifra_rsa.decrypt(chave_de_sessão_enc)
# Desencripta os dados com a chave de sessão AES
cifra_aes = AES.new(chave_de_sessão, AES.MODE_EAX, cifra_aes.nonce)
data2 = cifra_aes.decrypt_and_verify(texto_cifrado, tag)
print(data.decode("utf-8"))
In [ ]:
In [ ]: