em Blog Intl

É bastante comum o uso das chaves públicas/privadas do Bitcoin para assinar mensagens. Porém há muito pouco material na Internet exemplificando uma forma segura de criptografar informações utilizando as chaves que você já possui, ou criptografar uma mensagem para que outra pessoa possa descriptografar no destino.

Nos fóruns, encontramos muitos afirmando que não seria possível criptografar mensagens com esse algorítmo de criptografia de curva elíptica (ECC – Elliptic Curve Cryptography), pois ele teria sido ‘criado apenas para assinar mensagens’ e sugerem a utilização do PGP, que foi criado para essa finalidade (e cumpre muito bem o seu papel).

Mas se nós já temos em mãos uma tecnologia tão disruptiva, baseada em criptografia assimétrica, com um algorítmo superior aos tradicionais RSA e DSA com seus 4096 bit de tamanho (os algorítmos de curva elíptica conseguem garantir a mesma segurança com chaves dezenas de vezes menores, por exemplo com 256 bits de tamanho), porque teríamos que criar novos pares de chaves e ainda por cima ter o cuidado com a guarda das mesmas?

Depois de muita pesquisa, a conclusão a que chegamos foi que se ele pode ser utilizado para assinar mensagens, então por quê não poderia ser utilizado para criptografar também?

Por isso nós desenvolvemos uma prova de conceito que demonstra que é possível utilizar a criptografia assimétrica dos algorítmos de curva elíptica para criptografar uma mensagem utilizando a chave pública do endereço de destino e descriptografando a mesma mensagem utilizando a chave privada correspondente.

Para desenvolver este código, nós demos uma pesquisada nos protocolos utilizados pelo bitMessage, bitCry e pela wallet Electrum. Porém eles utilizam muitos recursos de bibliotecas próprias para Bitcoin e estávamos à procura de algo mais convencional, suportado pela própria linguagem de programação. Então tentamos simplificar ao máximo o conceito para que qualquer pessoa possa implementar a criptografia nos seus sistemas, principalmente se o sistema já utiliza Bitcoin em algum nível.

No Python, encontramos a lib pyelliptic, que trabalha diretamente com o algorítmos de criptografia de curva elíptica, já muito utilizado para assinar mensagens com as chaves públicas e privadas.

A prova de conceito que desenvolvemos pode ser encontrada diretamente no nosso GitHub.

A primeira parte do código é baseado na biblioteca pycoin, para testar o tipo de chave privada que está sendo submetida. Se você possuir as chave pública e/ou privada uncompressed do destino, o uso da biblioteca pycoin é totalmente desnecessário.

##
# Test and define what kind of secret exponent was provided
##
SEC_RE = re.compile(r"^(0[23][0-9a-fA-F]{64})|(04[0-9a-fA-F]{128})$")

def parse_as_number(s):
    try:
        return int(s)
    except ValueError:
        pass
    try:
        return int(s, 16)
    except ValueError:
        pass

def parse_as_secret_exponent(s):
    v = parse_as_number(s)
    if v and v < secp256k1._r:
        return v

secret_exponent = parse_as_secret_exponent(secret)
if secret_exponent:
    privkey = Key(secret_exponent=secret_exponent)

if SEC_RE.match(secret):
    privkey = Key.from_sec(unhexlify(secret))
else:
    try: 
        privkey = Key.from_text(secret)
    except encoding.EncodingError:
        pass

A segunda parte define as variáveis que serão utilizadas:

# Define vars automatically from privkey (could be manually, if you had the values)
privkey_uncompressed = '%x' % privkey.secret_exponent()
pubkey_uncompressed = hexlify(privkey.sec(use_uncompressed=True))

Se você já possui as chaves pública e privada, basta substituir diretamente e o código lá do início não será necessário.

Agora vamos preparar as chaves a serem utilizadas para criptografar e descriptografar mensagens. Elas precisam ser preparadas pois o protocolo do Bitcoin possui algumas especificidades em relação ao formato das chaves.

##
# Prepare pubkey for encrypting
##

pubkey_bin_tmp = arithmetic.changebase(pubkey_uncompressed[2:], 16, 256, minlen=64)
pubkey_bin = '\x02\xca\x00 '+ pubkey_bin_tmp[:32] + '\x00 ' + pubkey_bin_tmp[32:]

# Optionally you can use unhexlify, but need to add '\x20' after '\x00'
#pubkey_bin_tmp = unhexlify(pubkey_uncompressed)
#pubkey_bin = '\x02\xca\x00\x20' + pubkey_bin_tmp[1:-32] + '\x00\x20' + pubkey_bin_tmp[-32:]

##
# Prepare private key for decrypting
##

# Private Key to Bin
privkey_bin = '\x02\xca\x00\x20' + arithmetic.changebase(privkey_uncompressed, 16, 256, minlen=32)

# Optionally you can use unhexlify
#privkey_bin = '\x02\xca\x00\x20' + unhexlify(privkey_uncompressed)

Veja que há algumas opções de conversão de hexadecimal para binário:

  • A primeira utilizando arithmetic.changebase()
  • A segunda utilizando unhexlify()
  • E ainda há uma terceira opção, mas baseada na lib pycoin: h2b() e b2h()

Por fim, a aplicação mostra o resultado, criptografando uma mensagem e então exibindo-a descriptografada ao final.

##
# Outputs
##
print "Encrypt for wallet: %s" % privkey.address(use_uncompressed=False)
print "Uncompressed Hex Pubkey: %s" % pubkey_uncompressed
print "Message: ", message
print "\n"

encrypted = pyelliptic.ECC(curve='secp256k1').encrypt(message, pubkey_bin)
print "Hex Crypto Message: ", hexlify(encrypted)

decrypted = pyelliptic.ECC(curve='secp256k1', privkey=privkey_bin, pubkey=pubkey_bin).decrypt(encrypted)
print "Decrypted message: ", decrypted

DICA: Uma forma de conseguir a chave pública do endereço de destino, é utilizando a API do Blockchain.info, como no exemplo: https://blockchain.info/q/pubkeyaddr/1KFHE7w8BhaENAswwryaoccDb6qcT6DbYY.

Porém, à não ser que você seja o dono da wallet, só será possível encontrar a chave pública de endereços que já fizeram alguma transação pois as chaves públicas ficam registradas no blockchain. Se você for criptografar uma mensagem para uma wallet que nunca transacionou, terá:

  • que solicitar a chave pública diretamente para o dono ou,
  • solicitar ao destinatário para transferir alguns satoshis ao seu endereço para ter acesso à esta informação ou, 
  • recuperar a chave pública de uma mensagem assinada endereçada para você (contribuição passada pelo Felipe Micaroni, este processo é mais complexo porém plausível de acordo com a seção 4.1.6 deste paper).

É isso. Esperamos que tenha ajudado!

Você já conhece a nossa página no Facebook? Entra lá e deixa seu ‘Curtir’!

Você encontra este código completo no nosso GitHub.