Criptografia e fraude eleitoral no WhatsApp

Algumas pessoas acreditam que a criptografia de ponta a ponta do WhatsApp seja a culpada pelo aplicativo de mensagens ser uma terra sem leis pela qual está sendo manipulada a eleição no Brasil. Casey Newton, no The Verge, opinou que um aplicativo de mensagens deve ter ou criptografia segura ou mecanismos de compartilhamento que permitam que coisas viralizem, mas não as duas coisas.

A explicação pode parecer razoável à primeira vista, afinal a segurança do WhatsApp garante que nem mesmo o Facebook possa ler o conteúdo das mensagens que trocamos no aplicativo.

As questões com a criptografia de ponta a ponta não são exatamente novas. Em julho de 2016, uma juíza bloqueou o acesso de todo o país ao WhatsApp argumentando que “se as decisões judiciais não podem ser efetivamente cumpridas, o serviço não poderá ser mais prestado, sob pena de privilegiar inúmeros indivíduos que se utilizam impunemente do aplicativo WhatsApp para prática de crimes diversos”.

Vale voltar um pouco na história para lembrar que o WhatsApp implementou criptografia de ponta a ponta depois dos vazamentos de Edward Snowden, que mostraram que agências de inteligência americanas coletam comunicações realizadas em todo o mundo com fins comerciais e políticos. Tais vazamentos aumentaram o debate público sobre vigilância e privacidade na rede, debate que se aprofunda à medida que novos problemas aparecem, como fake news e agências de inteligência estrangeiras influenciando eleições, sequestros digitais como o que comprometeu o funcionamento do sistema de saúde britânico e ciberataques como o que atingiu fortemente os computadores da Ucrânia.

É preciso compreender que abrir brechas de segurança num aplicativo usado por mais de 1 bilhão de pessoas coloca a segurança de todos nós em risco. Para além de comprometer a privacidade das comunicações que fazemos pelo aplicativo (de mensagens entre amigos a senhas, números de cartão, fotos, localizações) e permitir que se use o conteúdo das nossas comunicações para vender propaganda, um aplicativo sem criptografia ajuda os poderosos que controlam a infra-estrutura de Internet a controlar nações inteiras e nunca é positivo para os ativistas. Como escreveu Julian Assange na época em que foram divulgados os documentos de Snowden: “A criptografia pode proteger não somente as liberdades civis e direitos dos indivíduos, mas a soberania e independência de países inteiros, solidariedade entre grupos com causas comuns e projetos de emancipação global.”

Pelo acúmulo que temos nos debates sobre a Internet e o ativismo digital, os que lutamos contra o imperialismo e defendemos Edward Snowden devemos defender a criptografia de ponta a ponta sem tergiversar.

A questão então é como impedir fraude nas eleições e a disseminação de desinformação num ambiente que não se consegue monitorar.

Um grupo de pesquisadores fez um interessante estudo sobre a disseminação de notícias falsas em grupos públicos e sugere três ideias que teriam que ser implementadas pelo próprio WhatsApp: restringir o número de contatos/grupos para os quais uma mensagem pode ser encaminhada de uma vez só de 20 para 5 (como foi feito na Índia), diminuir o número máximo de contatos em listas de transmissão (atualmente 256) e diminuir o número máximo de pessoas em grupos criados nos próximos dias (até o dia da eleição).

Essas ideias são, a meu ver, pouco efetivas e prejudiciais ao ativismo. A extrema-direita no Brasil constrói sua rede há anos e já possui muitos milhares de grupos, enquanto quem está criando novos grupos neste momento é justamente quem busca resistir ao bolsonarismo. Além disso, quem encaminha mensagens de forma manual ou por listas de transmissão, sem usar robôs, são as pessoas comuns e não as grandes máquinas. Parece que é a viralização entre pessoas comuns (que compartilham mensagens nos seus grupos de família, amigos, igreja, trabalho) que essas ideias querem combater. São sugestões que podem contribuir para diminuir o alcance de fake news (e também true news) nos próximos dias (ainda que dificilmente poderiam ser implementadas nesse tempo), mas não atacam o centro do problema.

A SaferNet sugere que se insiram mecanismos de fact checking dentro do aplicativo e que se avise ao usuário quando um conteúdo é falso por meio do uso de um hash para identificar arquivos de mídia. Essa ideia pode ser efetiva para o futuro, mas é necessário pensar com cuidado nas implicações que teria na segurança da criptografia. Ainda que se chegue à conclusão de que ela não compromete a nossa segurança, exige bastante cuidado e engenharia para ser implementada: seriam necessárias mudanças profundas tanto do lado do cliente (atualização de todos os aplicativos nos celulares de todas as pessoas) como do lado do servidor do WhatsApp. É impossível realizá-la nos menos de 7 dias que faltam até as eleições e não serve para fazer algum tipo de investigação retroativa.

Nesse conjunto de medidas paliativas, penso em outras duas que podem ser tomadas por empresas no Brasil e que também podem ajudar. A primeira é cumprir o princípio da neutralidade da rede, dessa forma evitando que sejam vendidos planos de Internet que privilegiam o tráfego do WhatsApp sobre os outros. Atualmente, o acesso a Internet de muitos brasileiros é limitado: se você só tem acesso gratuito ao WhatsApp e não a outros sites, é difícil verificar as informações que você recebe. A segunda depende da imprensa tradicional que tanto critica a proliferação de desinformação: elas deveriam retirar de seus sites os mecanismos de paywall que impedem as pessoas de ler o conteúdo das notícias e dessa forma colaboram para um ambiente no qual se lê apenas os títulos.

Para atacar o coração do problema, porém, minha opinião é que a reportagem de capa da Folha do dia 18/10 dá o caminho: é necessário denunciar e investigar profundamente as empresas que financiam, via caixa 2, o disparo de mensagens via WhatsApp, assim como as empresas que fazem esses disparos. A justiça deveria ir atrás do rastro do dinheiro e dos envolvidos nesse tipo de operação ilegal, como fizeram jornalistas da BBC para escrever uma brilhante reportagem que mostra o funcionamento de algumas dessas empresas. É preciso investigar para descobrir, combater e punir quem financia, quem cria e quem dispara as notícias falsas.

Para ajudar nessa investigação é bom lembrar que, embora o conteúdo de mensagens trocadas no WhatsApp seja protegido por criptografia de ponta a ponta, os metadados do sistema não são. Ou seja, é possível exigir do Facebook e ir atrás dos endereços de IP que usaram determinados números de telefone que comprovadamente espalham fake news, assim como saber quantas mensagens tais números enviaram, quando, para que números e que grupos, quantas pessoas tem nesses grupos etc. A polícia e a justiça brasileira sabem disso, por isso colocar a culpa em algum aspecto técnico da plataforma parece cortina de fumaça. Deveríamos responsabilizar as instituições brasileiras por resguardar a democracia no nosso país e não o Mark Zuckerberg, embora ele tenha afirmado no início do ano que faria tudo que fosse necessário para garantir a integridade das eleições no Brasil.

Investigar num ambiente com mensagens criptografadas sem dúvidas dá mais trabalho do que se toda a rede fosse completamente transparente e todas as nossas comunicações estivessem permanentemente violadas, mas é possível e necessário.

Publicado originalmente na Revista Movimento.

Novo cálculo eleitoral e algoritmos para distribuir vagas de eleições proporcionais

Um exercício matemático e computacional sobre os métodos usados para alocar vagas nas eleições proporcionais do Brasil. Estava refletindo sobre isso uns dias atrás e estou colocando aqui para sistematizar, compartilhar e saber o que outras pessoas acham. A epígrafe é: O quociente eleitoral está morto; vida longa à menor sobra!


O Código Eleitoral brasileiro determina a forma como são eleitos os candidatos a cargos proporcionais (deputados e vereadores) no seu capítulo IV. O método consiste, em resumo, em:

  1. Calcular o quociente eleitoral qq, definido como a soma do total de votos válidos (isso é, sem contar abstenções, brancos e nulos) tt dividida pela quantidade de vagas a preencher ss, arredondada para o número inteiro mais próximo. Isso é, q:=[ts]q := \left[\frac{t}{s}\right].
  2. Para cada partido ou coligação ii, calcular o quociente partidário pip_i, definido como o número de votos obtidos pelo partido ou coligação viv_i dividido (divisão inteira, ou seja, desprezada a fração) pelo quociente eleitoral qq. Isso é, pi:=viqip_i := \left\lfloor \frac{v_i}{q} \right\rfloor \forall i. Então, pip_i é a quantidade inicial de vagas atribuídas ao partido ou coligação ii.
  3. Dividir as sipis - \sum_{i} p_i vagas restantes de acordo com o Cálculo das Sobras, também conhecido como Cálculo das Médias. Tal método é definido pela repetição do seguinte processo enquanto houver vagas restantes (copiado da lei): “o número de votos válidos atribuídos a cada partido político ou coligação será dividido pelo número de lugares por eles obtidos mediante o cálculo do quociente partidário mais um, cabendo ao partido político ou à coligação que apresentar a maior média um dos lugares a preencher, desde que tenha candidato que atenda à exigência de votação nominal mínima”.

Para além desse algoritmo, vale fazer duas considerações importantes sobre o método que vão reaparecer em outros momentos deste texto:

  1. Até a eleição de 2016, os partidos ou coligações ii que não tivessem quociente partidário pi1p_i \geq 1 não participavam do Cálculo das Sobras. Tal restrição foi removida na reforma eleitoral de 2017.
  2. A partir da eleição de 2016, foi imposta uma cláusula de barreira (aprovada em 2015) que exige que candidatos tenham 10% do quociente partidário para poderem ser eleitos (a não ser no caso em que não há candidatos nessa condição). Esse fato vai ser desprezado nesse artigo. Ou seja, vamos assumir que todos os partidos ou coligações que tiverem uma vaga de acordo com o método têm também candidatos aptos a serem eleitos.

A implementação do método descrito acima em C++ (escolhi essa linguagem para usar a fila de prioridades da STL) seria como segue:

// Estrutura usada para ordenar partidos de acordo com suas sobras.
struct party {
  int index, votes, seats;
  bool operator < (const party &o) const {
    return votes / (double) (seats + 1) < o.votes / (double) (o.seats + 1);
  }
};

vector <int> codigoEleitoral(vector <int> v, int s) {
  // Pré-calculo de t, o número total de votos válidos.
  int t = 0;
  for (int i = 0; i < v.size(); i++) {
    t += v[i];
  }

  // Passo 1: calcular o quociente eleitoral.
  int q = round(t / (double) s);

  // Passo 2: calcular o quociente partidário e alocar primeiras vagas.
  priority_queue <party> Q;
  for (int i = 0; i < v.size(); i++) {
    s -= (int) (v[i] / q);
    Q.push((party) {i, v[i], v[i] / q});
  }

  // Passo 3: Cálculo das Sobras ou Médias.
  while (s--) {
    party p = Q.top();
    Q.pop();
    p.seats++;
    Q.push(p);
  }

  // Reconstrução do resultado.
  vector <int> result(Q.size(), 0);
  while (!Q.empty()) {
    party p = Q.top();
    result[p.index] = p.seats;
    Q.pop();
  }

  return result;
}

Sua complexidade é Θ((n+s)logn)\Theta((n+s) \log n), onde nn é o número de partidos ou coligações. O logaritmo vem do uso da heap implementada pela STL (priority_queue).

A partir dessas definições e desse método, gostaria de compartilhar alguns pensamentos.

Equivalência com D’Hondt

Primeiramente, é bom observar que o Método D’Hondt é exatamente o que é chamado de Cálculo das Médias no Código Eleitoral. Como descreve a Wikipedia:

O método [D’Hondt] consiste numa fórmula matemática, ou algoritmo, destinada a calcular a distribuição dos mandatos pelas listas concorrentes, em que cada mandato é sucessivamente alocado à lista cujo número total de votos dividido pelos números inteiros sucessivos, começando na unidade (isto é no número 1) seja maior. O processo de divisão prossegue até se esgotarem todos os mandatos e todas as possibilidades de aparecerem quocientes iguais aos quais ainda caiba um mandato.

A tabela abaixo, da distribuição de 8 vagas para 4 partidos, ajuda a visualizá-lo:

÷ 1 ÷ 2 ÷ 3 ÷ 4 ÷ 5 ÷ 6 ÷ 7 ÷ 8 Vagas
Partido A 100.000 50.000 33.333 25.000 20.000 16.666 14.286 12.500 4
Partido B 80.000 40.000 26.666 20.000 16.000 13.333 11.428 10.000 3
Partido C 30.000 15.000 10.000 7.500 6.000 5.000 4.286 3.750 1
Partido D 20.000 10.000 6.666 5.000 4.000 3.333 2.857 2.500 0

Mais interessante do que perceber que o Cálculo das Médias do Código Eleitoral é o Método D’Hondt, porém, é observar que o método completo brasileiro (não apenas o cálculo das médias) é equivalente matematicamente ao Método D’Hondt, isso é, os dois métodos distribuem as vagas da mesma forma.

Creio que a forma mais fácil de provar isso consista em demonstrar que um partido ou coligação tem uma vaga pelo Código Eleitoral se e somente se tem tal vaga pelo Método D’Hondt. Talvez mostrar que o D’Hondt e o Código Eleitoral decidem as ipi\sum_i p_i primeiras vagas da mesma forma? Será que não bastaria provar que o quociente eleitoral é maior ou igual ao menor número negrito na tabela do D’Hondt (conceito que chamaremos de menor sobra e definiremos melhor até o final desse artigo)? Intuitivamente isso parece não ser muito difícil, mas na prática não fiz e vou considerar que essa prova maravilhosa não cabe nas margens desse artigo.

O importante é que, aceitando esse fato, nota-se que o Código Eleitoral poderia ser simplificado deixando de lado o cálculo do quociente eleitoral e do quociente partidário, aplicando apenas o Cálculo das Sobras/Médias (ou Método D’Hondt) desde o princípio. Sua implementação poderia ser, assim, reduzida para:

vector <int> dhondt(vector <int> v, int s) {
  priority_queue <party> Q;
  for (int i = 0; i < v.size(); i++) {
    Q.push((party) {i, v[i], 0});
  }

  // Cálculo das Sobras ou Médias (D'Hondt).
  while (s--) {
    party p = Q.top();
    Q.pop();
    p.seats++;
    Q.push(p);
  }

  // Reconstrução do resultado.
  vector <int> result(Q.size(), 0);
  while (!Q.empty()) {
    party p = Q.top();
    result[p.index] = p.seats;
    Q.pop();
  }

  return result;
}

A complexidade computacional claramente seria a mesma, mas reduzir o esforço mental para entender o cálculo e também o código — evitando calcular os arredondamentos de quociente eleitoral e quociente partidário — parece bom.

A verdade é que isso nos motiva a perceber que a única utilidade do quociente eleitoral qq tem a ver com as duas considerações que fiz acima e informei que iriam aparecer em outros momentos do texto. De fato, em 2018, a única utilidade do quociente eleitoral será barrar candidatos que não tenham 10% desse quociente.

Já o quociente partidário pip_i teria importância se ainda houvesse a necessidade dos partidos ou coligações ii terem pi1p_i \geq 1 para participar do Cálculo das Médias. Sem tal mecanismo, já não serve para nada.

A menor sobra é o xis da questão

O quociente eleitoral tinha importância porque era nele que se devia mirar para eleger o primeiro candidato de um partido ou coligação. Com a mudança no Código Eleitoral, o número fundamental da eleição deixa de ser o quociente eleitoral e passa a ser a menor sobra xx, que defino como o menor número na tabela do Método D’Hondt que garante vaga.

Matematicamente, x:=minivirix := \min_i \frac{v_i}{r_i}, onde viv_i é a quantidade de votos do partido ou coligação ii e rir_i é o número de vagas alocadas ao partido ou coligação ii no final do processo.

A menor sobra não é importante apenas para que se saiba quantos votos são necessários para eleger o primeiro candidato de um partido ou coligação (como antes era o quociente eleitoral), mas para saber quantos candidatos cada partido ou coligação vai eleger. Com efeito, é fácil ver que o número de candidatos a serem eleitos por um partido ou coligação é vix\left\lfloor\frac{v_i}{x}\right\rfloor. Isso a torna especialmente interessante, muito mais útil do que o velho quociente.

Refletir sobre a importância da menor sobra sugere um algoritmo que evita a implementação do D’Hondt e da fila de prioridades: uma simples busca binária para encontrar xx, dado que quanto menor [maior] é xx mais [menos] candidatos são eleitos.

vector <int> menorSobra(vector <int> v, int s) {
  int a = 0, b = 0;

  // Encontrando o limite superior para a busca binária.
  for (int i = 0; i < v.size(); i++) {
    b = max(b, v[i] + 1);
  }

  // Busca binária para encontrar x.
  while ((b - a) > 1) {
    int x = (a + b) / 2, y = 0;
    for (int i = 0; i < v.size(); i++) {
      y += v[i] / x;
    }
    if (y < s) {
      b = x;
    } else {
      a = x;
    }
  }

  // Construção do resultado dividindo votos de cada partido por x.
  vector <int> result(v.size(), 0);
  int x = (a + b) / 2;
  for (int i = 0; i < v.size(); i++) {
    result[i] = v[i] / x;
  }

  return result;
}

Esse algoritmo tem complexidade Θ(nlogt)\Theta(n \log t), que me parece razoável.

Por tudo isso, exceto se a sua preocupação for com a cláusula de barreira de 2015 — isso é, com fazer seu candidato atingir 10% do quociente eleitoral —, pareceria que vale esquecer o quociente eleitoral e o algoritmo do TSE, dando centralidade à menor sobra.

Do ponto de vista prático, como, porém, estimar a menor sobra antes de ter os dados de uma eleição (que ainda não tenha acontecido, por exemplo)?

Bem, sabemos que ela é limitada superiormente pelo quociente eleitoral (xqx \leq q), que é fácil de estimar já que se conhece o tamanho do colégio eleitoral, o número de vagas a serem preenchidas e dá pra chutar uma proporção de votos válidos. Ao menos, é bem mais fácil do que estimar a configuração completa do resultado de uma eleição. Então, só por isso, talvez valha, ainda, não descartar o quociente eleitoral.

© 2005–2020 Tiago Madeira