Verificando um download
Um build reproduzível afirma isto veio deste código, e qualquer um pode conferir — que é a garantia que um projeto publicado anonimamente pode oferecer no lugar de um certificado de assinatura de código. Veja como cobrá-la.
Três verificações, e elas respondem a perguntas diferentes. Execute-as nesta ordem; cada uma vale menos sem a anterior.
| Verificação | O que ela prova | O que ela não prova |
|---|---|---|
| Checksum | O arquivo não foi alterado em trânsito. | Nada sobre quem o produziu, nem sobre o que há dentro dele. |
| Assinatura | A lista de checksums veio de quem detém a chave do projeto. | Que a chave pertença a alguém em quem você tenha razão para confiar. |
| Recompilação | O binário contém este código e nada mais. | Que o código seja honesto — leia-o, ou leia as revisões. |
O checksum#
curl -fsSLO https://zycord.com/releases//download/v<version>/SHA256SUMS.randomx
sha256sum --check --ignore-missing SHA256SUMS.randomx
# on macOS:
shasum -a 256 --check --ignore-missing SHA256SUMS.randomx
--ignore-missing não é opcional, e não é indulgênciaAs releases agora trazem apenas os arquivos -randomx, então
SHA256SUMS.randomx é a lista que cobre o que você baixou;
SHA256SUMS.deb e SHA256SUMS.desktop cobrem os pacotes Debian e a
carteira desktop. Cada uma cobre vários arquivos e você baixou um. Sem a flag, os cinco que você não tem são reportados como
FAILED open or read e o comando sai com código diferente de zero num download perfeitamente bom, na
própria página que diz a você que uma divergência significa um binário comprometido. Uma verificação cujo resultado normal é
uma falha é uma verificação que as pessoas aprendem a ignorar.
A assinatura#
As releases atualmente publicam o zycord-release-key.asc — a chave pública — mas
nada assinado com ela além do manifesto do atualizador: nenhum dos arquivos de checksum tem assinatura
destacada, e nenhum é assinado em claro. O gpg --verify abaixo, portanto, falha
hoje com um arquivo ausente, e isso é uma lacuna da release e não um erro seu. Até que ela seja fechada, a
verificação que de fato sustenta o argumento de confiança é a
reconstrução: ela não precisa de assinatura alguma, porque você produz os bytes você mesmo e compara.
Quando uma assinatura de checksum for publicada, ela será feita com a chave do projeto. A impressão digital completa é:
E724 39CE DD85 11F9 D607 550B 87FD 60D5 EB4A 0B29
Ela é publicada no cabeçalho do whitepaper, no depósito arquivado, e no anúncio de gênese. Ela nunca é rotacionada silenciosamente: uma chave que muda sem uma declaração assinada pela antiga é indistinguível de um comprometimento, e deve ser tratada como tal.
Obtenha a chave de onde for conveniente e então confira o que você recebeu contra a linha acima. Um arquivo de chave cuja impressão digital seja aquela é a chave do projeto, não importa qual host a entregou a você, e um cuja impressão digital seja qualquer outra coisa não vale nada, por mais confiável que o host parecesse.
Três lugares a carregam. Qualquer um deles serve:
# 1. The repository, if you have a clone. No network, no keyserver.
gpg --import packaging/zycord-release-key.asc
# 2. The release page, as an asset beside the archives.
curl -fsSLO https://zycord.com/releases//download/v<version>/zycord-release-key.asc
gpg --import zycord-release-key.asc
# 3. A keyserver. Use this one.
gpg --keyserver hkps://keyserver.ubuntu.com --recv-keys E72439CEDD8511F9D607550B87FD60D5EB4A0B29
Depois confirme o que entrou no seu chaveiro, antes de confiar nele:
gpg --fingerprint E72439CEDD8511F9D607550B87FD60D5EB4A0B29
keys.openpgp.org para esta chaveEle é o keyserver padrão em várias builds do GnuPG, e serve uma cópia reduzida: o pacote
de chave pública puro, sem user ID e sem autoassinatura. O GnuPG recusa essa cópia —
gpg: key 87FD60D5EB4A0B29: new key but contains no user ID - skipped — e a
chave nunca entra no chaveiro, então a verificação abaixo falha com o que parece uma assinatura ruim e
na verdade é uma chave ausente. Aquele servidor remove user IDs por política até que um endereço seja confirmado
através dele, o que não é algo que um projeto pseudônimo possa fazer.
gpg --verify SHA256SUMS.randomx.asc SHA256SUMS.randomx
Hoje isto reporta No such file or directory,
porque nenhuma release publicou esse arquivo ainda. Veja a nota no topo desta seção.
O gpg vai acrescentar que a chave não está certificada com uma assinatura confiável. Isso
é esperado e não é uma falha. Significa que você não disse ao seu chaveiro que
pessoalmente responde por esta chave, que é exatamente o que a impressão digital está aqui para resolver. O que
importa é que a assinatura seja verificada, e que a chave contra a qual ela foi verificada seja a impressão digital
impressa acima.
O whitepaper também é assinado#
O PDF servido a partir deste site tem uma assinatura destacada ao lado, feita pela mesma chave:
curl -fsSLO https://zycord.com/assets/zycord-whitepaper.pdf
curl -fsSLO https://zycord.com/assets/zycord-whitepaper.pdf.asc
gpg --verify zycord-whitepaper.pdf.asc zycord-whitepaper.pdf
A reconstrução — a verificação que de fato substitui um certificado#
Um hash diz que o arquivo não foi alterado em trânsito. Não diz nada sobre o que o publicador colocou nele. Isto diz:
git clone https://gitlab.com/zycord-group/zycord-node.git
cd zycord-node
git checkout v<version>
make build
sha256sum bin/zcd bin/zycordd
Compare com o SHA256SUMS.binaries da release. Eles precisam coincidir
exatamente. Se coincidirem, o binário que você baixou contém este código-fonte e nada mais
— uma afirmação que nenhum certificado jamais fez sobre coisa alguma.
O toolchain Go faz parte do que você está reproduzindo#
É o go1.26.2. O mesmo código-fonte compilado por duas releases diferentes do Go são dois
binários diferentes; isso não é uma sutileza, é mensurável — o próprio fluxo de release deste
repositório já registrou três valores SHA-256 distintos para um commit e um conjunto de flags, um por
versão do Go. Por isso o make build fixa GOTOOLCHAIN=go1.26.2 e para com uma
explicação se não puder usar esse toolchain, em vez de produzir uma divergência que parece a
adulteração que esta página inteira existe para detectar.
Duas consequências que vale ter em mente:
- Você não precisa confiar na versão escrita acima. O binário lançado a
declara:
go version -m zcdimprime o toolchain que o construiu, ao lado do módulo e das flags de build. Se essa linha e a fixação doMakefilealgum dia discordarem, o binário não foi construído do jeito que esta página diz. - Fazer checkout de uma tag mais antiga traz também a fixação dela junto com o código, então recompilar uma release antiga continua funcionando depois que o projeto migra para um Go mais novo.
As quebras de linha fazem parte do código-fonte aqui#
O wallet/webui embute o frontend da carteira com //go:embed, então as
quebras de linha desses arquivos são bytes no binário: um checkout convertido para CRLF constrói um
zcd diferente a partir do mesmo commit — uma divergência de causa inocente e
aparência assustadora. Um .gitattributes contendo * text=auto eol=lf dá LF na
árvore de trabalho em toda plataforma e em toda configuração do git, então um clone novo
dá a mesma resposta em qualquer lugar.
Dois casos que ele não cobre: reconstruir uma tag mais antiga que esse arquivo (defina
core.autocrlf=input antes do checkout), e um clone já existente feito com
core.autocrlf=true, que não se corrige com um pull.
-randomx não são reproduzíveis, por construçãoO RandomX é C++, então uma build com cgo carrega um toolchain C do sistema para dentro da saída e ninguém consegue
reconstruí-la byte a byte. Esses arquivos têm checksum no SHA256SUMS.randomx e
estão deliberadamente ausentes do SHA256SUMS.binaries, que lista o hash que uma build feita por
você a partir do código-fonte deve produzir. Este é o único ponto em que a
camada que entra numa rede e a camada que é atestada não se sobrepõem — veja a tabela das duas camadas
em Instalação.
Declarar isso é o ponto: é uma decisão sua em vez de uma suposição feita por você.
Confira a implementação contra o protocolo#
Separadamente de conferir a procedência de um binário, você pode conferir que ele implementa o protocolo. Os vetores dourados são o protocolo; uma implementação independente que passe por eles é um par, não um fork.
zcd vectors # check this build against spec/vectors
zcd genesis --testnet # rebuild block 0 and print its id
zcd params --testnet # print the frozen parameters
O zcd genesis é o comando que mais importa: um anúncio de lançamento se compromete, semanas
antes, com a tag do código, o hash dos parâmetros e o id de gênese — e isto reconstrói todos
eles em milissegundos, a partir do código-fonte, em qualquer máquina. Os valores da testnet pública estão na
página da testnet; uma build que imprima outros é uma build em uma
cadeia diferente, e ela não vai conectar.
Atestações independentes#
Uma pessoa reconstruindo uma tag prova que a tag é reproduzível. Vários desconhecidos reconstruindo-a e assinando o resultado provam bastante mais, e é o padrão para o qual projetos sérios convergiram exatamente por esse motivo. Reconstrua uma tag e publique o digest que você obteve e a tag que você construiu na thread do anúncio — inclusive quando não bater, que é justamente o caso que vale a pena ouvir.