Como gerar sua própria chave privada Bitcoin

Em criptomoedas, uma chave privada permite que um usuário obtenha acesso à sua carteira. A pessoa que detém a chave privada controla totalmente as moedas nessa carteira. Por esse motivo, você deve manter isso em segredo. E se você realmente quiser gerar a chave sozinho, faz sentido gerá-la de maneira segura.

Aqui, vou fornecer uma introdução às chaves privadas e mostrar-lhe como você pode gerar sua própria chave usando várias funções criptográficas. Vou fornecer uma descrição do algoritmo e do código em Python.

Preciso gerar uma chave privada?

Na maioria das vezes você não faz. Por exemplo, se você usar uma carteira da web como Coinbase ou Blockchain.info, eles criarão e gerenciarão a chave privada para você. É o mesmo para trocas.

Carteiras móveis e de mesa geralmente também geram uma chave privada para você, embora possam ter a opção de criar uma carteira a partir de sua própria chave privada.

Então, por que gerá-lo assim mesmo? Aqui estão as razões que eu tenho:

  • Você quer ter certeza de que ninguém conhece a chave
  • Você só quer aprender mais sobre criptografia e geração aleatória de números (RNG)

O que exatamente é uma chave privada?

Formalmente, uma chave privada para o Bitcoin (e muitas outras criptomoedas) é uma série de 32 bytes. Agora, existem várias maneiras de registrar esses bytes. Pode ser uma cadeia de 256 unidades e zeros (32 * 8 = 256) ou 100 rolos de dados. Pode ser uma string binária, uma string Base64, uma chave WIF , uma frase mnemônica ou, finalmente, uma string hexadecimal. Para nossos propósitos, usaremos uma string hexadecimal de 64 caracteres.

A mesma chave privada, escrita em diferentes formatos.

Por que exatamente 32 bytes? Ótima pergunta! Veja, para criar uma chave pública a partir de uma chave privada, o Bitcoin usa o ECDSA , ou Algoritmo de Assinatura Digital de Curva Elíptica. Mais especificamente, ele usa uma curva específica chamada secp256k1 .

Agora, essa curva tem uma ordem de 256 bits, recebe 256 bits como entrada e gera inteiros de 256 bits. E 256 bits são exatamente 32 bytes. Então, para colocar de outra forma, precisamos de 32 bytes de dados para alimentar este algoritmo de curva.

Existe um requisito adicional para a chave privada. Porque usamos ECDSA, a chave deve ser positiva e deve ser menor que a ordem da curva. A ordem de secp256k1 é FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 , o que é muito grande: quase qualquer número de 32 bytes será menor do que isso.

Método ingênuo

Então, como nós geramos um inteiro de 32 bytes? A primeira coisa que vem à mente é simplesmente usar uma biblioteca RNG no idioma de sua escolha. O Python até fornece uma maneira fofa de gerar bits suficientes:

 importação aleatória 
bits = random.getrandbits (256)
# 30848827712021293731208415302456569301499384654877289245795786476741155372082
bits_hex = hex (bits)
# 0x4433d156e8c53bf5b50af07aa95a29436f29a94e0ccc5d58df8e57bdc8583c32
private_key = bits_hex [2:]
# 4433d156e8c53bf5b50af07aa95a29436f29a94e0ccc5d58df8e57bdc8583c32

Parece bom, mas na verdade não é. Você vê, bibliotecas RNG normais não são destinadas à criptografia, pois elas não são muito seguras. Eles geram números com base em uma semente e, por padrão, a semente é a hora atual. Dessa forma, se você souber aproximadamente quando gerou os bits acima, tudo o que você precisa fazer é forçar algumas variantes.

Quando você gera uma chave privada, você quer ser extremamente seguro. Lembre-se, se alguém aprender a chave privada, pode facilmente roubar todas as moedas da carteira correspondente, e você não tem chance de recuperá-las.

Então, vamos tentar fazer isso de maneira mais segura.

RNG criptograficamente forte

Juntamente com um método RNG padrão, as linguagens de programação geralmente fornecem um RNG projetado especificamente para operações criptográficas. Este método é geralmente muito mais seguro, porque desenha entropia diretamente do sistema operacional. O resultado de tal RNG é muito mais difícil de reproduzir. Você não pode fazer isso conhecendo o tempo da geração ou tendo a semente, porque não há semente. Bem, pelo menos o usuário não insere uma semente – ao invés disso, é criado pelo programa.

Em Python, o RNG criptograficamente forte é implementado no módulo de secrets . Vamos modificar o código acima para tornar a geração da chave privada segura!

 segredos de importação 
bits = segredos.randbits (256)
# 46518555179467323509970270980993648640987722172281263586388328188640792550961
bits_hex = hex (bits)
# 0x66d891b5ed7f51e5044be6a7ebe4e2eae32b960f5aa0883f7cc0ce4fd6921e31
private_key = bits_hex [2:]
# 66d891b5ed7f51e5044be6a7ebe4e2eae32b960f5aa0883f7cc0ce4fd6921e31

Isso é incrível. Aposto que você não seria capaz de reproduzir isso, mesmo com acesso ao meu PC. Mas podemos ir mais fundo?

Sites especializados

Existem sites que geram números aleatórios para você. Vamos considerar apenas dois aqui. Um é o random.org , um conhecido gerador de números aleatórios de propósito geral. Outro é o bitaddress.org , que é projetado especificamente para geração de chave privada Bitcoin.

O random.org pode nos ajudar a gerar uma chave? Definitivamente, como eles têm serviço para gerar bytes aleatórios. Mas dois problemas surgem aqui. Random.org afirma ser um gerador verdadeiramente aleatório, mas você pode confiar nele? Você pode ter certeza de que é de fato aleatório? Você pode ter certeza de que o proprietário não registra todos os resultados de geração, especialmente os que se parecem com chaves particulares? A resposta é com você. Ah, e você não pode executá-lo localmente, o que é um problema adicional. Este método não é 100% seguro.

Agora, bitaddress.org é uma história totalmente diferente. É open source, então você pode ver o que está sob o capô. É do lado do cliente, para que você possa baixá-lo e executá-lo localmente, mesmo sem uma conexão com a Internet.

Então, como isso funciona? Ele usa você – sim, você – como fonte de entropia. Ele pede para você mover o mouse ou pressionar teclas aleatórias. Você faz isso por tempo suficiente para tornar inviável a reprodução dos resultados.

O processo de geração de uma entropia movendo aleatoriamente o mouse. O grande pedaço de símbolos mostra a piscina.

Você está interessado em ver como funciona o bitaddress.org ? Para fins educacionais, veremos seu código e tentaremos reproduzi-lo em Python.

Nota rápida: bitaddress.org fornece a chave privada em um formato WIF compactado, que é próximo ao formato WIF que discutimos anteriormente. Para os nossos propósitos, faremos o algoritmo retornar uma string hexadecimal para que possamos usá-lo posteriormente para uma geração de chave pública.

Bitaddress: os detalhes

O Bitaddress cria a entropia de duas formas: pelo movimento do mouse e pela pressão da tecla. Nós vamos falar sobre ambos, mas vamos nos concentrar nas teclas pressionadas, já que é difícil implementar o rastreamento do mouse na biblioteca do Python. Esperamos que o usuário final digite os botões até que tenhamos entropia suficiente e depois geraremos uma chave.

Bitaddress faz três coisas. Ele inicializa a matriz de bytes, tentando obter o máximo de entropia possível do seu computador, preenche a matriz com a entrada do usuário e gera uma chave privada.

O Bitaddress usa a matriz de 256 bytes para armazenar a entropia. Essa matriz é reescrita em ciclos, portanto, quando a matriz é preenchida pela primeira vez, o ponteiro vai para zero e o processo de preenchimento é iniciado novamente.

O programa inicia uma matriz com 256 bytes de window.crypto . Em seguida, ele grava um registro de data e hora para obter um adicional de 4 bytes de entropia. Por fim, obtém dados como o tamanho da tela, seu fuso horário, informações sobre plug-ins de navegador, sua localidade e muito mais. Isso dá outros 6 bytes.

Após a inicialização, o programa espera continuamente pela entrada do usuário para reescrever os bytes iniciais. Quando o usuário move o cursor, o programa grava a posição do cursor. Quando o usuário pressiona os botões, o programa escreve o código de caracteres do botão pressionado.

Finalmente, o bitaddress usa a entropia acumulada para gerar uma chave privada. Precisa gerar 32 bytes. Para esta tarefa, o bitaddress usa um algoritmo RNG chamado ARC4. O programa inicializa o ARC4 com a hora atual e a entropia coletada, depois obtém bytes um a um 32 vezes.

Isso é tudo uma simplificação de como o programa funciona, mas espero que você entenda. Você pode verificar o algoritmo detalhadamente no Github .

Fazendo você mesmo

Para nossos propósitos, criaremos uma versão mais simples do bitaddress. Primeiro, não coletamos dados sobre a máquina e a localização do usuário. Segundo, nós inseriremos a entropia apenas via texto, já que é bastante desafiador continuar recebendo a posição do mouse com um script Python (verifique PyAutoGUI se você quiser fazer isso).

Isso nos leva à especificação formal de nossa biblioteca de geradores. Primeiro, ele inicializará uma matriz de bytes com RNG criptográfico, preencherá o registro de data e hora e, finalmente, preencherá a string criada pelo usuário. Depois que o pool de sementes for preenchido, a biblioteca permitirá que o desenvolvedor crie uma chave. Na verdade, eles poderão criar quantas chaves privadas quiserem, tudo garantido pela entropia coletada.

Inicializando o pool

Aqui nós colocamos alguns bytes do RNG criptográfico e um timestamp. __seed_int e __seed_byte são dois métodos auxiliares que inserem a entropia em nosso array de pool. Observe que nós usamos secrets .

 def __init_pool (self): 
para i no intervalo (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

Semeando com entrada

Aqui, primeiro colocamos um timestamp e, em seguida, a string de entrada, caractere por caractere.

 def seed_input (self, str_input): 
time_int = int (time.time ())
self .__ seed_int (time_int)
para char em str_input:
char_code = ord (char)
self .__ seed_byte (char_code)

Gerando a chave privada

Esta parte pode parecer difícil, mas na verdade é muito simples.

Primeiro, precisamos gerar um número de 32 bytes usando nosso pool. Infelizmente, não podemos apenas criar nosso próprio objeto random e usá-lo apenas para a geração de chaves. Em vez disso, há um objeto compartilhado usado por qualquer código em execução em um script.

O que isso significa para nós? Isso significa que a cada momento, em qualquer lugar do código, uma simples random.seed(0) pode destruir toda a nossa entropia coletada. Nós não queremos isso. Felizmente, o Python fornece métodos getstate e setstate . Então, para salvar nossa entropia toda vez que geramos uma chave, lembramos do estado em que paramos e a definimos na próxima vez que queremos fazer uma chave.

Segundo, apenas nos certificamos de que nossa chave esteja no alcance (1, CURVE_ORDER ). Este é um requisito para todas as chaves privadas do ECDSA. O CURVE_ORDER é a ordem da curva secp256k1, que é FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 .

Finalmente, por conveniência, convertemos para hexadecimal e tira a parte '0x'.

 def generate_key (self): 
big_int = self .__ generate_big_int ()
big_int = big_int% (self.CURVE_ORDER - 1) # chave <ordem da curva
big_int = big_int + 1 # chave> 0
chave = hexadecimal (big_int) [2:]
chave de retorno
 def __generate_big_int (self): 
if self.prng_state é nenhum:
seed = int.from_bytes (self.pool, byteorder = 'grande', assinado = Falso)
random.seed (semente)
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, é muito simples: você pode gerar uma chave privada em três linhas de código!

 kg = KeyGenerator () 
kg.seed_input ('Cadeia verdadeiramente aleatória. Lancei um dado e consegui 4.')
kg.generate_key ()
# 60cf347dbc59d31c1358c8e5cf5e45b822ab85b79cb32a9f3d98184779a9efc2

Você pode ver você mesmo. A chave é aleatória e totalmente válida. Além disso, toda vez que você executa esse código, obtém resultados diferentes.

Conclusão

Como você pode ver, existem muitas maneiras de gerar chaves privadas. Eles diferem em simplicidade e segurança.

Gerar uma chave privada é apenas um primeiro passo. A próxima etapa é extrair uma chave pública e um endereço de carteira que você pode usar para receber pagamentos. O processo de gerar uma carteira é diferente para Bitcoin e Ethereum, e eu planejo escrever mais dois artigos sobre esse tópico.

Se você quiser brincar com o código, eu o publiquei neste repositório do Github .