Chave privada geração
A afirmação de que "perdendo a chave privada é possível fazer outra" é parcialmente verdadeira, dependendo do contexto em que estamos falando sobre **chaves privadas**, que são fundamentais no funcionamento de **criptomoedas** e sistemas de segurança baseados em criptografia. Vou explicar mais detalhadamente:
### O que é uma chave privada?
Uma **chave privada** é uma sequência de números e letras criptografadas, usada como uma senha para acessar ou controlar recursos em sistemas criptográficos, como criptomoedas (por exemplo, Bitcoin, Ethereum), carteiras digitais e outras aplicações de segurança.
### **Perder a chave privada**
Se você perder a chave privada associada a uma **carteira de criptomoeda**, por exemplo, **não poderá acessar seus fundos**. As chaves privadas são únicas e não há como recuperá-las sem uma cópia ou backup. Isso ocorre porque, no modelo descentralizado das criptomoedas, não existe uma autoridade central ou servidor para restaurar ou recuperar a chave.
### **"Fazer outra" chave privada**
No caso de **criptomoedas**, se você perder a chave privada **não há como gerar outra chave para acessar os mesmos fundos**. O que você pode fazer, no entanto, é **criar uma nova chave privada** e **uma nova carteira**. Se você tiver feito backups adequados (por exemplo, com uma **seed phrase**, ou frase de recuperação), poderá **recuperar sua carteira** e obter uma nova chave privada, mas somente se tiver esses backups.
#### Algumas possibilidades, dependendo do contexto:
1. **Se você tem a seed phrase**: Se você perdeu a chave privada mas ainda tem a **seed phrase** (uma sequência de palavras gerada no momento da criação da carteira), pode gerar uma nova chave privada e acessar sua carteira novamente, restaurando todos os fundos.
2. **Se você não tem a seed phrase e perdeu a chave privada**: Nesse caso, **não há como recuperar a chave privada ou acessar os fundos**. A perda de chave privada sem backup (como uma seed phrase) significa que os fundos são **permanentemente inacessíveis**.
3. **Outros sistemas (não apenas criptomoedas)**: Em outros sistemas de criptografia, pode ser possível **gerar uma nova chave privada** se você tiver algum tipo de mecanismo de recuperação, como um arquivo de backup, ou se o sistema permitir a criação de novas credenciais sem a necessidade de uma chave anterior. Porém, a perda de uma chave privada geralmente implica em perda permanente de acesso aos recursos ou dados associados, a menos que existam métodos alternativos de recuperação (como a utilização de um servidor de recuperação ou uma autoridade de certificação).
### Resumo:
- **Chave privada única**: Para criptomoedas e muitos sistemas de segurança, **a chave privada não pode ser "substituída"** se for perdida, a menos que você tenha feito backups adequados, como uma seed phrase.
- **Nova chave**: Você pode **gerar uma nova chave privada**, mas ela será **associada a uma nova carteira ou sistema**, e não aos recursos da carteira original (a menos que tenha backup).
Portanto, se você **perdeu sua chave privada** e não tem um backup adequado, **não será possível recuperar os recursos associados a ela**. Por isso, é sempre fundamental fazer **backups seguros** de suas chaves privadas ou seed phrases.
O que exatamente é uma chave privada?
Formalmente, uma chave privada para Bitcoin (e muitas outras criptomoedas) é uma série de 32 bytes. Hoje em dia, existem muitas maneiras de gravar esses bytes. Eles podem ser gravados como uma sequência de caracteres (em inglês, string) de 256 zeros e uns (32 * 8 = 256) ou 100 jogadas de dados. Pode ser uma string binária, uma string de base 64, uma chave WIF (texto em inglês), uma frase mnemônica (texto em inglês) ou ainda uma string hexadecimal. Neste artigo, usaremos uma string hexadecimal de 64 caracteres.
A mesma chave privada, escrita em formatos diferentes.
Por que exatamente 32 bytes? Ótima pergunta! Para criar uma chave pública a partir de uma privada, Bitcoin usa o ECDSA, ou Algoritmo de Assinatura Digital de Curva Elíptica (em inglês, Elliptic Curve Digital Signature Algorithm). Mais especificamente, usa uma curva particular chamada secp256k1.
Essa curva tem um arranjo de 256 bits, tem uma entrada de 256 bits e uma saída contendo números de 256 bits. 256 bits são exatamente 32 bytes. Isso quer dizer que precisamos de 32 bytes de dados para alimentar esse algoritmo de curva.
Existe um requisito adicional para uma chave privada. Por usarmos ECDSA, a chave deve ser positiva e inferior ao arranjo da curva. O arranjo de secp256k1 é FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141, o que é relativamente grande: quase todo número de 32 bytes será menor que ele.
Modo iniciante
Então, como geramos um número de 32 bytes? A primeira coisa que nos vem à mente é usar uma biblioteca RNG na linguagem de sua escolha. Python até oferece uma maneira bonita de gerar esses números com apenas alguns bits necessários:
import random
bits = random.getrandbits(256)
# 30848827712021293731208415302456569301499384654877289245795786476741155372082
bits_hex = hex(bits)
# 0x4433d156e8c53bf5b50af07aa95a29436f29a94e0ccc5d58df8e57bdc8583c32
private_key = bits_hex[2:]
# 4433d156e8c53bf5b50af07aa95a29436f29a94e0ccc5d58df8e57bdc8583c32
Parece bom, mas na verdade não é. Bibliotecas RNG comuns não foram criadas para criptografia. Sendo assim, elas não são muito seguras. Elas geram números baseados em uma semente (em inglês seed), que por padrão, é a hora atual. Sendo assim, se você souber aproximadamente quando eu gerei os bits acima, tudo o que você precisa é tentar um ataque de força bruta com algumas variantes.
Quando você gera uma chave privada, deseja estar extremamente seguro. Lembre-se de que, se alguém souber a chave privada, poderá facilmente roubar todas as moedas da carteira correspondente. Você não terá chance nenhuma de recuperá-las de volta.
Então, vamos tentar uma maneira mais segura.
RNG criptograficamente forte
Junto com o método básico de RNG, linguagens de programação normalmente fornecem um RNG desenhado especificamente para operações criptográficas. Esse método é usualmente muito mais seguro, pois extrai entropia diretamente do sistema operacional. O resultado desse RNG é muito mais difícil de reproduzir. Você não pode obtê-lo apenas sabendo a hora que foi gerado ou sua seed, pois não há nenhuma seed. Bom, ao menos o usuário não insere nenhuma seed. Ela é criada pelo programa.
Em Python, um RNG criptograficamente forte é implementado pelo módulo secrets. Vamos modificar o código acima para tornar a geração da chave privada mais segura!
import secrets
bits = secrets.randbits(256)
# 46518555179467323509970270980993648640987722172281263586388328188640792550961
bits_hex = hex(bits)
# 0x66d891b5ed7f51e5044be6a7ebe4e2eae32b960f5aa0883f7cc0ce4fd6921e31
private_key = bits_hex[2:]
# 66d891b5ed7f51e5044be6a7ebe4e2eae32b960f5aa0883f7cc0ce4fd6921e31
Isso é incrível. Eu aposto que você não seria capaz de reproduzi-lo, mesmo com acesso ao meu PC. Podemos, no entanto, ir mais longe.
Sites especializados
Existem sites que geram esses números aleatórios para você. Vamos analisar apenas dois aqui. Um é o random.org, um gerador de números bem conhecido. Outro é o bitaddress.org, que é desenhado especificamente para geração de chaves privadas de Bitcoin.
O random.org poderianos ajudar a gerar uma chave? Com certeza, já que eles têm um serviço para geração de bytes aleatórios. Porém, dois problemas emergem aqui. O random.org afirma ser um gerador verdadeiramente aleatório, mas será que você pode confiar? Você consegue ter certeza que é, de fato, aleatório? Como ter certeza de que o proprietário não grava todos os resultados gerados, especialmente os que se parecem com chaves privadas? Essa resposta fica a seu critério. Ah, você também não pode executá-lo localmente, o que gera um problema adicional. Esse método, definitivamente, não é 100% seguro.
Já com o bitaddress.org, a história é completamente diferente. Ele é de código aberto (em inglês open source). Então, você pode checar o que há debaixo dos panos. É client-side, ou seja você pode fazer o download e executá-lo localmente, até mesmo sem conexão a internet.
Então, como funciona? Ele usa você – sim, você – como uma fonte de entropia. Ele pede que você mova seu mouse ou pressione teclas aleatórias. Você realiza esses passos até que seja inviável reproduzir os resultados.
O processo de gerar uma entropia ao mover o mouse aleatoriamente. Os inúmeros símbolos mostram o agrupamento.
Tem interesse em descobrir como o bitaddress.org funciona? Para fins educacionais, vamos dar uma olhada no seu código e tentar reproduzi-lo em Python.
Nota rápida: o bitaddress.org fornece a chave privada em formato de WIF comprimido, que é parecido ao formato WIF (texto em inglês) que discutimos previamente. Para nossos propósitos, faremos o algoritmo retornar uma string hexadecimal. Dessa maneira, poderemos usá-la mais tarde na geração de uma chave pública.
Bitaddress: as especificidades
O Bitaddress cria uma entropia em duas maneiras: pelo movimento do mouse e pelo pressionamento das teclas. Vamos falar sobre ambos, mas vamos focar no pressionamento das teclas, já que é difícil de implementar o rastreamento do mouse na biblioteca do Python. Esperaremos o usuário apertar teclas suficientes até que tenhamos entropia suficiente. Então, geraremos uma chave.
O Bitaddress faz três coisas. Ele inicializa um array de bytes, tentando obter o máximo de entropia possível de seu computador, preenche o array com inserções do usuário (em inglês, inputs) e, então, gera uma chave privada.
O Bitaddress usa o array de 256 bytes para guardar a entropia. Esse array é reescrito em ciclos. Então, quando o array é preenchido pela primeira vez, o ponteiro vai para zero e o processo de preenchimento começa de novo.
O programa inicia um array com 256 bytes a partir de window.crypto. Então, ele escreve um número representativo de um momento específico (nesse caso, a hora atual – em inglês, timestamp) para obter 4 bytes adicionais de entropia. Por fim, ele obtém dados como o tamanho da tela, seu fuso horário, informações sobre os plugins do seu navegador, sua localização e mais. Isso resulta em outros 6 bytes.
Depois da inicialização, o programa continuamente espera os inputs do usuário para reescrever os bytes iniciais. Quando o usuário move o cursor, o programa escreve a posição do cursor. Quando o usuário pressiona teclas, o programa escreve o código da tecla pressionada.
Finalmente, o bitaddress usa a entropia acumulada para gerar uma chave privada. É preciso gerar 32 bytes. Para essa tarefa, o bitaddress usa um algoritmo RNG chamado ARC4. O programa inicializa o ARC4 com a hora atual e a entropia coletada e, então, coleta os bytes um por um 32 vezes.
Isso tudo é uma simplificação de como o programa funciona, mas eu espero que tenha dado uma ideia de como ele é. Você pode checar o algoritmo completo e seus detalhes no Github.
Faça você mesmo
Para nossos propósitos, construiremos uma versão mais simples do bitaddress. Não vamos coletar dados sobre a localização e a máquina do usuário, somente inseriremos a entropia via texto, visto que é bem desafiador receber continuamente a posição do mouse em um script do Python (dê uma olhada em PyAutoGUI se tiver interesse em fazer isso).
Isso nos traz para especificação formal de nossa biblioteca geradora. Primeiro, vamos inicializar um array de byte com RNG criptográfico. Depois, preencheremos com a timestamp e, finalmente, preencheremos com os caracteres inseridos pelo usuário. Depois que o agrupamento de seeds for preenchido, a biblioteca deixará o desenvolvedor criar a chave. Na verdade, o desenvolvedor será capaz de criar quantas chaves privadas ele quiser – tudo seguro de acordo com a entropia coletada.
Inicializando o agrupamento
Aqui, colocamos alguns bytes de RNG criptográfico e uma timestamp. __seed_int e__seed_byte são dois métodos auxiliares que inserem entropia ao nosso array agrupador. Note que usamos a biblioteca secrets.
def __init_pool(self):
for i in range(self.POOL_SIZE):
random_byte = secrets.randbits(8)
self.__seed_byte(random_byte)
time_int = int(time.time())
self.__seed_int(time_int)
def __seed_int(self, n):
self.__seed_byte(n)
self.__seed_byte(n >> 8)
self.__seed_byte(n >> 16)
self.__seed_byte(n >> 24)
def __seed_byte(self, n):
self.pool[self.pool_pointer] ^= n & 255
self.pool_pointer += 1
if self.pool_pointer >= self.POOL_SIZE:
self.pool_pointer = 0
Alimentando com input
Aqui, primeiro colocamos uma timestamp e, então, inserimos os caracteres, um a um.
def seed_input(self, str_input):
time_int = int(time.time())
self.__seed_int(time_int)
for char in str_input:
char_code = ord(char)
self.__seed_byte(char_code)
Gerando a chave privada
Essa parte pode parecer difícil, mas, na verdade, é bem simples.
Primeiro, precisamos gerar um número de 32 bytes usando nosso agrupador. Infelizmente, não podemos apenas criar nosso próprio objeto random e usá-lo somente para a geração da chave. Ao invés disso, existe um objeto compartilhado que é usado por qualquer código que está sendo executado em um único script.
O que isso significa para nós? Significa que a cada momento, em qualquer lugar do código, um simples random.seed(0) pode destruir toda nossa entropia coletada. Não queremos isso. Por sorte, o Python nos dá os métodos getstate e setstate. Dessa maneira, para salvar nossa entropia cada vez que geramos uma chave, nós lembramos o estado onde paramos e o configuramos na próxima vez que necessitamos fazer uma chave.
Em segundo lugar, queremos ter certeza de que nossa chave está em uma variação específica (1, CURVE_ORDER). Isso é um requisito para todas chaves privadas ECDSA. O CURVE_ORDER é o arranjo da curva secp256k1, que é FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141.
Por fim, por conveniência, convertemos para hexadecimal e cortamos a parte '0x'.
def generate_key(self):
big_int = self.__generate_big_int()
big_int = big_int % (self.CURVE_ORDER — 1) # key < curve order
big_int = big_int + 1 # key > 0
key = hex(big_int)[2:]
return key
def __generate_big_int(self):
if self.prng_state is None:
seed = int.from_bytes(self.pool, byteorder=’big’, signed=False)
random.seed(seed)
self.prng_state = random.getstate()
random.setstate(self.prng_state)
big_int = random.getrandbits(self.KEY_BYTES * 8)
self.prng_state = random.getstate()
return big_int
Em ação
Vamos tentar usar a biblioteca. Na verdade, é bem simples: você pode gerar uma chave privada em três linhas de código!
kg = KeyGenerator()
kg.seed_input(‘Uma frase completamente aleatória. Eu joguei um dado e obtive 4.’)
kg.generate_key()
# 60cf347dbc59d31c1358c8e5cf5e45b822ab85b79cb32a9f3d98184779a9efc2
Veja você mesmo. A chave é aleatória e totalmente válida. Além disso, cada vez que você rodar o código, obterá um resultado diferente.
Conclusão
Como pudemos ver, existem muitas maneiras de se gerar uma chave privada. Elas diferem em simplicidade e segurança.
Gerar uma chave privada é apenas o primeiro passo. O próximo passo é extrair uma chave pública e uma carteira que será usada para receber seus pagamentos. O processo de gerar uma carteira difere um pouco para Bitcoin e Ethereum. Eu planejo escrever outros dois artigos explorando mais esse tópico.
Se você quiser brincar com o código, ele está nesse repositório do Github.
O autor também trabalhou em um curso de criptomoedas. A primeira parte (texto em inglês) é uma descrição detalhada de blockchain.
Ele também publica pensamentos aleatórios sobre criptografia e criptomoedas no Twitter. Talvez você queira conferir.