A coleta de dados agregados é uma das indústrias mais lucrativas na computação, mas como podemos coletar dados de forma eficiente sem violar a privacidade do usuário? Bem, um Protocolo de Agregação Distribuída Multi-partidária (DAP) é uma resposta, e o método PRIO3 é um dos melhores para isso.
Embora a segurança tenha sido aplicada aos dados em repouso e no ar, muitas vezes ainda fazemos nossos cálculos em dados de texto não criptografado. Isso abre muitos problemas, especialmente que o processador pode determinar PII (Personally Identifiable Information) e coisas como chaves de criptografia secretas. Uma abordagem é usar MPC (Multi-party Computation), e onde podemos dividir nossa computação em blocos menores e dar a cada uma das partes uma parte da computação. Uma das melhores aplicações disso é coletar estatísticas de maneira consciente da privacidade.
Por exemplo, um fabricante de telefones celulares pode querer determinar o equilíbrio de gênero para os proprietários de seus telefones, mas não revelar nenhuma pessoa específica. Para isso, cada dispositivo cliente cria um SNIP (secret-shared non-interactive proof) para seu gênero. Dentro do PRIO3, esta é uma prova de conhecimento zero rápida e eficiente. Então, os servidores podem receber esses SNIPs e verificar sua validade. Uma vez verificados, eles podem então atualizar seus acumuladores locais para a agregação atual de estatísticas. Então, quando queremos revelar o equilíbrio de gênero dos dispositivos cliente, eles podem então publicar seus totais de agregação:
No geral, nenhum dos servidores pode determinar se o cliente é homem ou mulher, pois uma prova de conhecimento zero de seu gênero é criada; no entanto, ainda podemos calcular o total. Com o PRIO3, nenhum dos servidores chega a ver dados de texto não criptografado, e é baseado em um artigo clássico [here][1]:
A implementação do PRIO3 pode ser vista como geralmente parte de um Protocolo de Agregação Distribuída Multi-partidária (DAP) para medição de privacidade, e que agora está progredindo para padronização com o IETF [here]:
Usaremos o PRIO3 para criar um número de shares para valores Booleanos (contagem) e, em seguida, agregar os dados de volta. A análise central é uma contagem básica sobre se algo é Verdadeiro ou Falso, e o conjunto de dados é {true, false, true, true} e contará o número de valores verdadeiros [here]:
package main
import (
"fmt"
"crypto/rand"
"os"
"io"
"strconv"
"github.com/cloudflare/circl/vdaf/prio3/count"
)
func fromReader[T any](r io.Reader) (z T) {
switch zz := any(&z).(type) {
case *count.Nonce:
_, _ = r.Read(zz[:])
case *count.VerifyKey:
_, _ = r.Read(zz[:])
}
return
}
var Context = []byte("Test")
func main() {
NumShares := uint8(2)
input := []bool{true,false,true, true}
argCount := len(os.Args[1:])
if argCount > 0 {
a,_ := strconv.Atoi(os.Args[1])
NumShares=uint8(a)
}
c, _ := count.New(NumShares, Context)
params := c.Params()
shares := params.Shares()
aggShares := make([]count.AggShare, shares)
for i := range aggShares {
aggShares[i] = c.AggregateInit()
}
for _, mi := range input {
nonce := fromReader[count.Nonce](rand.Reader)
verifyKey := fromReader[count.VerifyKey](rand.Reader)
randb := make([]byte, params.RandSize())
_, _ = io.ReadFull(rand.Reader, randb)
var pubShare count.PublicShare
var inputShares []count.InputShare
pubShare, inputShares, _ = c.Shard(mi, &nonce , randb)
var prepStates []*count.PrepState
var outboundPrepShares []count.PrepShare
for i := range shares {
state, share, _ := c.PrepInit(&verifyKey, &nonce, i, pubShare, inputShares[i])
prepStates = append(prepStates, state)
outboundPrepShares = append(outboundPrepShares, *share)
}
var prepMsg *count.PrepMessage
prepMsg, _ = c.PrepSharesToPrep(outboundPrepShares)
var outShare *count.OutShare
for i := range shares {
outShare,_ = c.PrepNext(prepStates[i], prepMsg)
c.AggregateUpdate(&aggShares[i], outShare)
}
}
numMeas := uint(len(input))
aggResult, _ := c.Unshard(aggShares, numMeas)
fmt.Printf("Inputs: %v\n\n", input)
fmt.Printf("Number of inputs: %d\n\n", numMeas)
fmt.Printf("Number of shares: %d\n\n", NumShares)
fmt.Printf("Aggregated Count %d\n\n", *aggResult)
for i := range shares {
fmt.Printf("Share %v\n", aggShares[i])
}
}
E uma execução de amostra [here]:
Inputs: [true false true true]
Number of inputs: 4
Number of shares: 4
Aggregated Count 3
Share {[[7227276485192055052]]}
Share {[[673113590378828220]]}
Share {[[14679250781088487564]]}
Share {[[14313847295054699691]]}
Um exemplo de soma está aqui:
PRIO3 for Verifiable Distributed Aggregation Functions for Summation with GoE para um histograma:
PRIO3 for Verifiable Distributed Aggregation Functions for Histograms with Go[1] Corrigan-Gibbs, H., & Boneh, D. (2017). Prio: Private, robust, and scalable computation of aggregate statistics. In 14th USENIX symposium on networked systems design and implementation (NSDI 17) (pp. 259–282).