From 2934cb21f15e0457e93ac6c3186e36e45faecd85 Mon Sep 17 00:00:00 2001 From: dkvhr Date: Tue, 16 Jul 2024 02:02:58 -0300 Subject: [PATCH] Adicionei a parte 1 do blogpost de zero knowledge. Tambem adicionei a imagem que esta na pagina. --- .../blog/2024-07-15-intro-zero-knowledge.md | 118 ++++++++++++++++++ static/images/Alibaba_cave.png | Bin 0 -> 11792 bytes 2 files changed, 118 insertions(+) create mode 100644 content/blog/2024-07-15-intro-zero-knowledge.md create mode 100644 static/images/Alibaba_cave.png diff --git a/content/blog/2024-07-15-intro-zero-knowledge.md b/content/blog/2024-07-15-intro-zero-knowledge.md new file mode 100644 index 0000000..6a3663c --- /dev/null +++ b/content/blog/2024-07-15-intro-zero-knowledge.md @@ -0,0 +1,118 @@ +--- +title: "Introdução a provas de zero-knowledge - Parte 1" +date: 2024-07-15 +published: true +description: "Parte 1 de um estudo sobre ZKPs. A parte 2 irá conter a implementação desses protocolos utilizando Circom." +author: dkvhr +tags: + - "Criptografia" + - "ZK" +categories: + - "Easy" +--- + +_Essa é a parte 1 de um estudo sobre ZKPs. A parte 2 irá conter a implementação desses protocolos utilizando Circom._ +# O que são Zero-Knowledge Proofs? +ZK Proofs (ou em português algo como "provas de conhecimento zero") são o que o próprio nome diz: provas de algo ou alguma coisa que não demonstram nenhum "conhecimento" adicional ao verificador. + +### Um exemplo +Um exemplo bem simples pode ser visto no jogo da caverna: + +![Alibaba_cave](/images/Alibaba_cave.png) + +Nesse exemplo, Peggy e Victor sabem que existe uma _magic door (porta mágica)_ que precisa de uma senha para ser aberta no meio da caverna circular. O que acontece aqui é que se Peggy escolher o caminho A e sair pela entrada do caminho B, ela teria que passar por essa porta. Dessa forma, isso prova a Victor que Peggy *sabe* a senha, mas ele não obtém nenhuma informação sobre ela ou qualquer outra coisa durante a interação. + +### Definição +Uma definição um pouco melhor é a seguinte:\ +Uma ZKP é um protocolo entre ($P$, $V$) (sendo $P$ o provador e $V$ o verificador) que é um algoritmo _[PPT (probabilistic polynomial time)](https://en.wikipedia.org/wiki/PP_(complexity))_.\ +Um input público comum é o $x$ ($P$ quer provar que $x$ é, de fato, um membro de uma linguagem $L$).\ +Um input privado é o $W$, a _witness (testemunha)_ + +## Propriedades +### Completude +Se $x \in L$, $P$ possui uma testemunha $W$ **correta** e $(P, V)$ são honestos, $V$ vai aceitar o protocolo. +### Soundness +Se $x \notin L$, $\forall$ algoritmo $P*$, $V$ vai rejeitar o protocolo com probabilidade $P$ (_soundness parameter_) estabelecida. Idealmente queremos $P = 1 - negl(n)$. Veja mais em [negligible functions](https://www.youtube.com/watch?v=l5A3oEG-XKk) +### Intuição de zero-knowledge +1. O algoritmo PPT $V*$ **não aprende nada** com o protocolo. +2. Tudo o que $V*$ aprende, $V*$ poderia ter computado sozinho. +3. $\exists$ um algoritmo PPT $S$ que $V*$ poderia usar para gerar o mesmo conhecimento ($\tau$). Basicamente $S$ é um simulador. +4. $\exists$ um algoritmo PPT $S$ (público) que gera uma sequência de $\tau_{sim}$ de forma que: +{$\tau_{sim}$} é [computacionalmente indistinguível](https://en.wikipedia.org/wiki/Computational_indistinguishability) de {$\tau_{real}$} + +## Protocolo de exemplo +Agora que temos uma definição um pouco mais formal, podemos olhar para um protocolo que utiliza de zero-knowledge proofs. + +### Isomorfismo de grafos +Sejam $G_1, G_2$ dois grafos presumidamente isomorfos. Queremos provar que sabemos um isomorfismo entre esses dois grafos sem mostrar esse isomorfismo. +Vamos definir nossos parâmetros públicos como $X$, sendo ele um par de grafos $({G_0}$ e ${G_1})$. Nosso parâmetro privado (nossa testemunha ${W}$) como sendo a permutação $\Pi$ que leva ao isomorfismo entre $({G_0}$ e ${G_1})$. De forma que $G_1 = \Pi(G_0)$ e $G_0 = \Pi^{-1}(G_1)$. Chamaremos quem quer provar o isomorfismo de $P$ e o verificador de $V$. + + +Como o protocolo começa: + +* $P$ escolhe um valor de $b$ aleatoriamente de {$0, 1$} +* $P$ computa uma permutação aleatória $\sigma$ que pode ser aplicada a qualquer um dos grafos +* $P$ calcula $G = \sigma(G_0)$ se $b=0$ e calcula $G = \sigma(G_1)$ se $b=1$. +* $P$ envia $G$ para $V$ +* $V$ escolhe um valor de $b'$ aleatoriamente de $\{0, 1\}$ e envia para $P$ + * Se $b = b'$, $P$ envia $\phi$ = $\sigma$ a $V$ + * Se $b=1, b'=0$, $P$ envia $\phi$ = $\sigma \circ \Pi$ + * Se $b=0, b'=1$, $P$ envia $\phi$ = $\sigma \circ \Pi^{-1}$ +* $V$ recebe $\phi$ e faz a verificação: + * $G$ $\stackrel{?}{=}$ $\phi(G_{b'})$ + * Se a verificação se confirmar, $V$ aceita. Caso contrário, ele rejeita + +### Completude + +Para verificar a completude, podemos ver que se $b \neq b'$, então $\phi(G_{b'})$=$\sigma(G_b)$ + +Se $b = b'$, apesar de não verificarmos o isomorfismo, verificamos que $P$ não está sendo desonesto ao escolher valores de $G_0$ ou $G_1$ na hora de mandar o $G$ para $V$. + +### Soundness + +Se $b \neq b'$ e $b'=0$, então:\ +$G \stackrel{?}{=} \phi(G_0)$\ +$G \stackrel{?}{=} \sigma \circ \Pi(G_0)$\ +$G \stackrel{?}{=} \sigma(G_1)$\ +Que se conclui como verdadeiro. + +Agora para $b'=1$:\ +$G \stackrel{?}{=} \phi(G_1)$\ +$G \stackrel{?}{=} \sigma \circ \Pi^{-1}(G_1)$\ +$G \stackrel{?}{=} \sigma(G_0)$\ +Que também é verdadeiro. + +Agora para $b = b'$:\ +Se $b = b'$, $P*$ vai **falhar** com uma probabilidade de 1/2. Ao rodar o protocolo várias vezes, a possibilidade de cair nesse caso é praticamente zero.\ +Mais formalmente temos:\ +Rode o protocolo $n$ vezes. Aceite a prova somente se todas as verificações forem aceitas. O _soundness parameter_ será: $P = 1 - 1/2^n$ + +É importante escolhermos um valor de $\sigma$ novo a cada interação do protocolo. Se escolhermos o mesmo valor, $V*$ pode pedir o isomorfismo entre $G$ e $G_0$ e depois pedir o isomorfismo entre $G$ e $G_1$. A partir daí o verificador teria o isomorfismo entre $G_0$ e $G_1$ _(yikes!)_. + +## Protocolo de identificação de Schnorr +Agora que já vimos um pouco de como funcionam as ZK-Proofs, podemos dar uma breve olhada em mais um exemplo que também é zero-knowledge. +### Como funciona +Seja $E$ uma curva elíptica com ponto gerador $G$ e de ordem $q$. Suponha uma chave privada $x$ e uma chave pública $X$, sendo $X = xG$. Queremos provar o conhecimento da chave privada sem dizer mais nada sobre ela. Isto é, usando zero-knowledge. + +Começamos o protocolo com o provador $(P)$ gerando um número $r$ aleatoriamente escolhido de $\mathbb{Z}_{q}$.\ +Depois disso, $P$ calcula $R = rG$ e envia para o verificador $(V)$\ +$V$ recebe o valor de $R$ e calcula um valor de $c$ aleatoriamente escolhido de um subconjunto de desafios denominado $Challenge\ space$\ +$V$ envia $c$ para $P$.\ +$P$ calcula $e = r + cx (mod\ q)$ e envia isso para $V$\ +$V$, por fim, multiplica $e$ por $G$ e verifica se isso se iguala a $R + cX$:\ +$eG \stackrel{?}{=} R + cx$\ +Caso ocorra uma confirmação, $V$ aceita o protocolo e acredita em $P$ + +## Conclusão + +Falaremos em mais detalhes sobre o protocolo de Schnorr na parte 2, onde utilizaremos Circom para implementar isso aí e o protocolo de isomorfismo de grafos. Até lá~ ^^ + +# Referências +[1] https://www.youtube.com/watch?v=_MYpZQVZdiM\ +[2] https://en.wikipedia.org/wiki/PP_(complexity)\ +[3] https://www.youtube.com/watch?v=l5A3oEG-XKk\ +[4] Neal Koblitz. A Course in Number Theory and Cryptography\ +[5] [Dan Boneh, Victor Shoup. A Graduate Course in Applied Cryptography](https://crypto.stanford.edu/~dabo/cryptobook/BonehShoup_0_4.pdf)\ +[6] [Yehuda Lindell. How To Simulate It – A Tutorial on the Simulation Proof Technique](https://eprint.iacr.org/2016/046.pdf)\ +[7] [Anotações de aula 18 de CS6180: Theory of computing da Cornell University](http://www.cs.cornell.edu/courses/cs6810/2009sp/scribe/lecture18.pdf)\ +[8] [Nguyen Thoi Minh Quan. Intuitive Advanced Cryptography](https://github.com/cryptosubtlety/intuitive-advanced-cryptography/blob/master/advancedcrypto.pdf) diff --git a/static/images/Alibaba_cave.png b/static/images/Alibaba_cave.png new file mode 100644 index 0000000000000000000000000000000000000000..d42bcaa0e1b1f8271d9308ad3d419270c69f3057 GIT binary patch literal 11792 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 literal 0 HcmV?d00001