Criptografia 101: Assinaturas de Limite
Este é parte de uma série de artigos sobre criptografia. Se este é o primeiro artigo que você encontra, eu recomendo começar do início da série.
No artigo anterior, aprendemos sobre polinômios e vimos algumas de suas aplicações.
No entanto, esta nova peça de criptografia parece desconectada de tudo que aprendemos até agora. Ainda assim, há maneiras interessantes de combiná-la com conceitos anteriores, como assinaturas digitais.
Este artigo será dedicado exclusivamente a explicar um exemplo disso, onde combinaremos as técnicas de assinatura usuais com polinômios, para criar um esquema híbrido interessante. Trabalharemos no contexto de curvas elípticas, e usaremos e para denotar geradores de grupo para um grupo .
Basearei minha explicação vagamente neste paper, com alguns ajustes na esperança de tornar as coisas mais fáceis de navegar.
Ainda assim, um aviso amigável: assinaturas de limite não são simples de explicar. Há muitas nuances que precisaremos esclarecer. Então, vamos primeiro ver um breve resumo do que elas são e o que fazem, antes de realmente mergulhar na matemática por trás delas.
Assinantes Suficientes
Assinaturas de limite são um tipo de multissinatura — significando que múltiplos participantes são necessários para assinar uma mensagem. Mas desta vez, não é necessário que todos os participantes façam isso.
Imagine um grupo de dez pessoas que têm privilégios de administrador para uma aplicação. Para realizar alguma ação sensível, exigimos pelo menos três aprovações.
Dessa forma, não precisamos incomodar todos os administradores ao mesmo tempo; apenas um subconjunto de assinantes disponíveis será suficiente.
Isso parece uma melhoria ao esquema de multissinatura que exploramos anteriormente, onde todos os assinantes eram obrigatórios a participar. Mas na realidade, alcançar este resultado envolve flexionar alguns músculos criptográficos mais.

Podemos dividir assinaturas de limite em três passos principais ou algoritmos (como a maioria dos esquemas de assinatura):
- Geração de chaves (KeyGen), que é um algoritmo que produz um par de chaves privada compartilhada e pública,
- Assinatura, onde uma mensagem é processada junto com a chave privada e um nonce para obter um par ,
- E verificação, o passo onde a assinatura é validada contra a mensagem e a chave pública.
Quando trabalhamos com esquemas de assinatura de limite, os passos de geração de chaves e assinatura, que eram bastante diretos em exemplos passados, serão substituídos por processos mais complexos envolvendo comunicações entre os assinantes. Felizmente, a verificação permanece a mesma — então nosso foco ficará nos dois primeiros passos.
Note que a ideia de exigir um limite de assinantes é muito reminiscente da quantidade mínima de pontos para reconstruir um polinômio via interpolação. E de fato, isso está no núcleo de como assinaturas de limite funcionam.
Além disso, para manter as coisas claras, devemos dizer que os assinantes ou participantes são ordenados. Cada um terá um identificador ou índice, variando de a , o número total de participantes.
Nossos objetivos estão definidos, e a introdução acabou. Vamos ao que interessa?

Preliminares
Em protocolos de limite, compartilhar informação é fundamental. Em última análise, a capacidade de um conjunto de assinantes compartilhar informação é o que permitirá que eles produzam assinaturas.
Já vimos que compartilhar um segredo pode ser alcançado com Compartilhamento de Segredo de Shamir (SSS). A ideia era avaliar um polinômio em muitos valores diferentes, e compartilhar os resultados como pontos. E com esses pontos, poderíamos interpolar de volta o polinômio original.
Mas há um problema. Como qualquer receptor pode verificar se os valores que eles recebem estão calculados corretamente? Ou, em outras palavras, há um jeito de provar que os pontos estão efetivamente relacionados ao polinômio original?
E você pode estar se perguntando por que os valores estariam incorretos? Por que alguém enviaria um valor errado? Há pelo menos duas razões a considerar: erros na comunicação e atividade maliciosa. É possível que um atacante possa estar tentando quebrar nosso modelo de assinatura — não podemos necessariamente esperar que todos se comportem corretamente, e devemos tomar ação para mitigar este possível cenário.
Para abordar esta preocupação, precisaremos de uma nova ferramenta.
Compartilhamento de Segredo Aleatório Verificável
O que fazemos é pedir ao compartilhador para fazer um compromisso. Isso vinculará o compartilhador à sua informação secreta (seu polinômio), para que depois eles simplesmente não possam produzir valores inválidos.
Esta ideia é o que está por trás do Compartilhamento de Segredo Aleatório Verificável (VRSS), uma espécie de substituto direto para SSS. O que queremos fazer é nos comprometer com os coeficientes do nosso polinômio — e para isso, precisaremos não de um, mas dois deles:
Por que, você pergunta? Porque compromissos precisam ser ocultadores. Não queremos revelar os coeficientes, então seus compromissos devem ser cegados. Os coeficientes do segundo polinômio são de fato esses fatores de cegamento!
Então, usando nossos geradores de grupo, cada participante calcula e transmite os valores de compromisso , um para cada um dos coeficientes nos polinômios:
Legal! Agora tudo que resta é compartilhar. E para isso, todos precisarão avaliar seu polinômio. Como cada participante tem um índice , podemos fazer a escolha de avaliar a parte para um jogador alvo no seu índice, então .
O que isso significa é que indivíduos receberão e de algum outro participante .

E ao receber estes valores, o participante pode validá-los da seguinte forma:
Isso é basicamente tudo! Agora temos um mecanismo para compartilhar informação secreta, de uma forma que é verificável. Com esta ferramenta, podemos começar a construir nosso esquema de assinatura.
Geração de Chaves
Como há múltiplas partes envolvidas em assinaturas de limite, cada participante terá um inteiro como sua parte (ou pedaço) de uma chave privada.
No entanto, este não é um inteiro escolhido aleatoriamente, como de costume. Em vez disso, os participantes se envolvem em um processo onde interagem uns com os outros, produzindo finalmente sua parte da chave privada. Esses tipos de protocolos são chamados algoritmos de Geração de Chave Distribuída (DKG).
E podemos usar VRSS para construir nosso algoritmo. Que conveniente!

A ideia é que cada participante receberá uma parte de cada um de seus pares, e eles usarão estes valores verificados para calcular sua parte da chave privada:
É possível que alguns valores não passem na verificação; algumas partes podem ser desqualificadas neste caso. É por isso que VRSS é importante.
Ainda assim, vamos seguir o caminho feliz para manter as coisas relativamente simples.
A saída do DKG é um pedaço de uma chave privada compartilhada, . Nenhuma das partes envolvidas neste protocolo conhece este valor — apenas seus pedaços.

Finalmente, precisamos de uma chave pública. E para isso, cada participante transmite seu coeficiente secreto independente ou zero, . Este segredo não pode ser divulgado como tal, e portanto, é transmitido como uma parte da chave pública:
Acho bastante estranho ver a parte da chave pública sendo calculada assim. Aposto que você esperava ver ali!
Há uma boa razão para não ser usado, no entanto. Voltaremos a esta afirmação mais tarde, porque precisaremos definir algumas coisas para entender o que realmente está acontecendo.

Uma vez que todas as partes são públicas, então cada participante pode calcular a chave pública global independentemente:
Para finalizar, aqui está um breve resumo para este passo:
A geração de chaves envolve partes se comunicando umas com as outras, gerando pedaços de uma chave privada compartilhada. Nenhum jogador conhece a chave privada compartilhada. Uma chave pública que está associada com o segredo compartilhado também é calculada.
Com todas as chaves no lugar, tudo que resta é assinar.
Assinando uma Mensagem
A primeira coisa que precisaremos é uma mensagem para assinar. Isso não é trivial, no entanto, porque todos precisam concordar com uma mensagem. Não cobriremos como isso acontece — vamos apenas assumir que todos conhecem a mensagem sendo assinada.
No ECDSA, um assinante tipicamente escolheria um nonce aleatório , e calcularia um desafio correspondentemente, produzindo uma assinatura assim:
Mas como já vimos, esta não é como a criptografia de limite tende a operar. Em vez disso, um grupo de assinantes se comunicará uns com os outros para produzir uma assinatura. E a primeira coisa que eles precisarão é de um nonce.
Felizmente, já temos uma ferramenta para gerar um valor distribuído: DKG! Vamos apenas dizer que assinantes executam uma rodada de DKG, obtendo uma parte , e um desafio associado:
Para a computação da assinatura, todos usarão a coordenada x de , que denotaremos .
Construindo a Assinatura
Como você provavelmente pode adivinhar a esta altura, a geração da assinatura também é feita em partes. E novamente, a ideia é que apenas quando um certo número de partes está disponível, seremos capazes de produzir uma assinatura válida através da agregação dessas partes calculadas independentemente.
Nosso requisito é o seguinte: as partes da assinatura devem interpolar para a assinatura final — que deve passar na verificação quando usando a chave pública . A coisa mais natural a fazer aqui é adaptar a expressão para para que use partes de seus componentes em vez disso:
Mas isso faz sentido? Aqui, temos uma adição, multiplicações, e até inversos modulares. Não parece óbvio assumir que isso funcionará assim mesmo.
Parece justo examinarmos esta expressão e verificar que funciona adequadamente. E realmente, não é tão complicado quanto você imaginaria. Vamos devagar, um passo de cada vez.

Interpolando Adições
Para nos começar, digamos que temos dois polinômios e . Se os avaliarmos em diferentes valores , obtemos conjuntos de pontos da forma e . Por conveniência, vamos apenas denotá-los e :
Sabemos que qualquer subconjunto de t pontos desses pontos nos permite interpolar de volta os polinômios originais. E se definirmos o termo independente de como , poderíamos escrever que:
Como lembrete, no contexto de compartilhamento de segredo, geralmente estamos interessados no termo independente. É por isso que quando dizemos que interpolamos alguns valores, podemos nos referir à saída como apenas este coeficiente independente ou zero, e não ao polinômio inteiro. Mas realmente, a saída completa é o polinômio inteiro!
Similarmente, vamos assumir que temos pontos onde tem termo independente , e então:
Agora, o que acontece se somarmos os polinômios e ?

Podemos somar termo por termo, e terminamos com um polinômio com o mesmo grau dos originais (), mas onde o termo independente é . Além disso, como , então todos os pontos que interpolam para , que são , podem ser calculados como . E então:
E isso é incrível! Isso significa que podemos essencialmente somar partes de um polinômio, e quando interpolando, obteremos como resultado a soma dos segredos compartilhados. Legal!
Agora podemos analisar o cálculo da chave pública. Lembre-se de que a parte é calculada como a soma dos valores .
Por causa disso, é essencialmente uma parte de uma soma de polinômios, cujo termo independente será a soma de todos os termos . O que significa que o resultado de interpolar todos os produzirá essa soma!

E essa é a razão pela qual a chave pública é calculada do jeito que é. Tudo se encaixa!
Interpolando Produtos
Produtos são ligeiramente mais complicados. Quando multiplicamos e , o polinômio resultante terá o dobro do grau. E por causa disso, precisamos de duas vezes tantos pontos para interpolação:
E isso não é realmente ótimo, porque agora precisamos de mais valores para interpolar, o que significa mais comunicações entre pares.
Independentemente deste inconveniente, a boa notícia é que isso se comporta do jeito que esperamos: quando multiplicamos , os termos independentes também são multiplicados, e nossos valores também interpolarão para !
Também vale mencionar: quando multiplicamos partes por uma constante, a interpolação resultante também será multiplicada por ela:

Então se tomarmos nossas partes como , então a interpolação produzirá . Bem direto, né?

Tudo bem, só temos mais um caso para analisar. A dor e o sofrimento acabarão prontamente, eu prometo.
Interpolando Inversos
Realmente, tudo que está faltando é lidar com aquele maldito inverso modular. O que queremos é produzir valores que, quando interpolados, produzam . Isso vai levar alguns passos. É.
- Primeiro, todos executarão uma rodada de VRSS para produzir partes . Claro, essas partes interpolam assim:
- Em seguida, cada participante calculará e transmitirá:
- Como é o resultado de uma multiplicação, ao receber partes, qualquer um poderia interpolar este valor:
- Finalmente, cada participante calcula desta forma:
Como essa magia funciona, você pergunta? Bem, considere isso: age como um termo constante quando interpolando os valores . E por causa disso, terminamos com:
E como mágica, construímos valores que interpolam para o inverso de !

Isso é tudo que vamos precisar! Vamos verificar de volta nosso cálculo de assinatura com nossas conclusões recém-encontradas.
De Volta à Assinatura
Se pudéssemos reconstruir cada segredo compartilhado, então o cálculo da assinatura aconteceria como no ECDSA padrão:
Mas na prática, não queremos que isso aconteça — e só temos partes. E então nos perguntamos se isso:
também interpolou para . E a resposta é um retumbante sim, porque estamos apenas lidando com adições, produtos e inversos — e já sabemos como estes se comportam.
Talvez o único problema aqui seja que como estamos lidando com um produto de partes (o termo ), precisaremos de como partes para interpolar. Mas deixando isso de lado, temos certeza de que interpolar os valores produzirá o valor esperado de !
Protocolos diferentes podem fazer uso de várias técnicas para tentar mitigar a necessidade de pontos extras para interpolação — e idealmente, gostaríamos de manter esse número o mais próximo de possível. Além disso, quanto menos passos de comunicação forem necessários, melhor.
Para finalizar, quando cada participante calcula sua parte , eles simplesmente a transmitem. E quando partes suficientes estão disponíveis, qualquer um pode interpolar, produzir e gerar .
E aí está! Simples, né?

Estou brincando, claro — isso é qualquer coisa menos simples. Mas a ideia geral é o que realmente é a conclusão chave:
Durante a assinatura, partes novamente se comunicam umas com as outras, gerando pedaços de uma assinatura compartilhada. Quando pedaços suficientes estão disponíveis, a assinatura final pode ser construída; com menos pedaços do que necessário, simplesmente não é possível.
A verificação acontece como de costume, porque a saída da assinatura é simplesmente o par .
Resumo
Acho que este é o artigo mais tecnicamente carregado que escrevi até agora. Tentei ao máximo mantê-lo simples, mas há algumas coisas que simplesmente não podemos evitar explicar. No mínimo, espero que isso esclareça alguns aspectos que, pela minha experiência, geralmente não são explicados em detalhes.
🔥 Importante: Na verdade há uma vulnerabilidade bem grande no processo que descrevi, onde partes da chave privada vazam quando compartilhando .
Isso é abordado no paper que usei como guia, e a solução é na verdade bem simples. Então por favor, não vá usar este artigo para construir suas assinaturas de limite — e talvez consulte o paper real em vez disso!
Projetar esse tipo de protocolos de Computação Multi-Partes, onde há comunicação entre partes, requer considerar que atividade maliciosa pode existir. Então em geral, esses protocolos são cheios de rodadas de desqualificação, provas de correção, talvez até alguma encriptação, e outras coisas divertidas. Realmente, eles são uma consolidação de muitas técnicas diferentes, e tendem a ser complexos.
Computação Multi-Partes é, em geral, bem complexa.
Este é realmente o esquema mais simples que consegui encontrar em termos de que ferramentas são necessárias, e é apresentado principalmente na esperança de ilustrar os componentes principais dessas técnicas. E isso significa que quanto mais ferramentas pudermos acumular, mais fácil será criar esquemas mais complexos.
Dito isso, nossa jornada continuará apresentando outra ferramenta extremamente poderosa, que desbloqueará novos tipos de funcionalidade: emparelhamentos. Vejo vocês no próximo!