Verificando commits git assinados?

95

Com as versões mais novas git, é possível assinar commits individuais (além de tags) com uma chave PGP:

git commit -m "some message" -S

E você pode mostrar essas assinaturas na saída do git logcom a --show-signatureopção:

$ git log --show-signature
commit 93bd0a7529ef347f8dbca7efde43f7e99ab89515
gpg: Signature made Fri 28 Jun 2013 02:28:41 PM EDT using RSA key ID AC1964A8
gpg: Good signature from "Lars Kellogg-Stedman <[email protected]>"
Author: Lars Kellogg-Stedman <[email protected]>
Date:   Fri Jun 28 14:28:41 2013 -0400

    this is a test

Mas existe uma maneira de verificar programaticamente a assinatura em um determinado commit, além de fazer um grep na saída de git log? Estou procurando o commit equivalente a git tag -v- algo que fornecerá um código de saída indicando se havia ou não uma assinatura válida em um determinado commit.

larsks
fonte
1
Eu acho que deveria ser git commit ...e git log .... Pelo que eu sei, gpgnão adicionou subcomandos que sejam passados ​​de forma gittransparente ... Não tenho nenhum repositório para testar, mas git show --show-signature <commitish>funciona?
twalberg
show_signatureapenas adiciona coisas à saída (consulte github.com/git/git/blob/master/log-tree.c#L370 ).
Emil Sit
Nota: em breve você terá --rawpara git verify-tag/ git verify-commit. Veja minha resposta abaixo
VonC
1
Nota: Com git 2,11 (Q4 2016), git logcódigos de status introduz adicionais E, X, Y, Rpara ERRSIG, EXPSIG, EXPKEYSIGe REVKEYSIG, de modo que um usuário de %G?fica mais informações. Veja minha resposta editada abaixo
VonC
1
Com Git 2.26 (Q1 2020), a nova configuração gpg.minTrustLevelpode ajudar ao usar git verify-tag/ verify -commit. Veja minha resposta editada abaixo .
VonC

Respostas:

114

Apenas no caso de alguém chegar a esta página através de um mecanismo de busca, como eu: Novas ferramentas foram disponibilizadas nos dois anos desde que a pergunta foi postada: Agora existem comandos git para esta tarefa: git verify-commite git verify-tagpodem ser usados ​​para verificar commits e tags, respectivamente.

Tarleb
fonte
34

Nota: até git 2.5, git verify-commite git verify-tagexibia apenas uma mensagem legível por humanos.
Se você quiser automatizar a verificação, git 2.6+ (Q3 2015) adiciona outra saída.

Consulte commit e18443e , commit aeff29d , commit ca194d5 , commit 434060e , commit 8e98e5f , commit a4cc18f , commit d66aeff (21 de junho de 2015) por brian m. Carlson ( bk2204) .
(Fundido por Junio ​​C Hamano - gitster- no commit ba12cb2 , 03 de agosto de 2015)

verify-tag/verify-commit : adicionar opção para imprimir informações de status gpg brutas

verify-tag/ verify-commitpor padrão exibe saída legível por humanos em erro padrão.
No entanto, também pode ser útil obter acesso às informações brutas de status do gpg, que podem ser lidas pela máquina, permitindo a implementação automatizada da política de assinatura .

Adicione uma --rawopção para fazerverify-tag produzir as informações de status do gpg no erro padrão em vez do formato legível por humanos.

Mais:

verify-tagsai com sucesso se a assinatura for boa, mas a chave não for confiável. verify-commitsai sem sucesso.
Essa divergência de comportamento é inesperada e indesejada.
Como verify-tagexistia anteriormente, adicione um teste de falha para ter o comportamento do verify-commitcompartilhamento verify-tag.


git 2.9 (junho de 2016) atualize o documento git merge :

Veja o commit 05a5869 (13 de maio de 2016) de Keller Fuchs (``) .
Auxiliado por: Junio ​​C Hamano ( gitster) .
(Fundido por Junio ​​C Hamano - gitster- no commit be6ec17 , 17 de maio de 2016)

--verify-signatures:
--no-verify-signatures:

Verifique se o commit de ponta do branch secundário que está sendo mesclado está assinado com uma chave válida, ou seja, uma chave que tenha um uid válido: no modelo de confiança padrão, isso significa que a chave de assinatura foi assinada por uma chave confiável.
Se o commit de ponta do branch lateral não for assinado com uma chave válida, a fusão é abortada
.


Atualizar Git 2.10 (terceiro trimestre de 2016)

Consulte commit b624a3e (16 de agosto de 2016) por Linus Torvalds ( torvalds) .
(Fundido por Junio ​​C Hamano - gitster- no commit 83d9eb0 , 19 de agosto de 2016)

gpg-interface: prefira a saída de formato de chave "longo" ao verificar assinaturas pgp

"git log --show-signature " e outros comandos que exibem o status de verificação da assinatura PGP agora mostram o ID-chave mais longo, como o ID-chave de 32 bits é no século passado.

O original de Linus foi reformulado para aplicar à trilha de manutenção apenas no caso de distribuidores binários que estão presos no passado queiram levá-lo para sua base de código mais antiga.


Git 2.11+ (quarto trimestre de 2016) será ainda mais preciso.

Veja o commit 661a180 (12 de outubro de 2016) de Michael J Gruber ( mjg) .
(Fundido por Junio ​​C Hamano - gitster- no commit 56d268b , 26 de outubro de 2016)

O status de verificação do GPG mostrado no %G?especificador de formato " " bonito não era rico o suficiente para diferenciar uma assinatura feita por uma chave expirada, uma assinatura feita por uma chave revogada, etc.
Novas letras de saída foram atribuídas para expressá-las .

De acordo com o gpg2doc/DETAILS :

Para cada assinatura apenas um dos códigos GOODSIG, BADSIG, EXPSIG, EXPKEYSIG, REVKEYSIGou ERRSIGserá emitido.

A git pretty-formatdocumentação agora inclui:

  • '%G? ': mostrar
    • "G " para uma assinatura boa (válida),
    • "B " para uma assinatura ruim,
    • "U " para uma boa assinatura com validade desconhecida,
    • "X " para uma assinatura válida que expirou,
    • "Y " para uma boa assinatura feita por uma chave expirada,
    • "R " para uma boa assinatura feita por uma chave revogada,
    • " E" se a assinatura não pode ser verificada (por exemplo, chave ausente) e "N" para nenhuma assinatura

Git 2.12 (1º trimestre de 2017) " git tag" e " git verify-tag" aprenderam a colocar o status de verificação do GPG em seu --format=<placeholders>formato de saída " " .

Consulte commit 4fea72f , commit 02c5433 , commit ff3c8c8 (17 de janeiro de 2017) por Santiago Torres ( SantiagoTorres) .
Consulte commit 07d347c , commit 2111aa7 , commit 94240b9 (17 de janeiro de 2017) por Lukas Puehringer (``) .
(Incorporado por Junio ​​C Hamano - gitster- no commit 237bdd9 , 31 de janeiro de 2017)

Adicionar --formata git tag -vsilencia a saída padrão da verificação GPG e, em vez disso, imprime o objeto de tag formatado.
Isso permite que os chamadores verifiquem o tagname de refs / tags com o tagname do cabeçalho do objeto tag na verificação GPG.


O Git 2.16 (primeiro trimestre de 2018) permitirá que a verificação da assinatura do commit seja ainda mais automatizada, com o merge.verifySignatures variável de configuração.

Consulte commit 7f8ca20 , commit ca779e8 (10 de dezembro de 2017) de Hans Jerry Illikainen (``) .
(Incorporado por Junio ​​C Hamano - gitster- no commit 0433d53 , 28 de dezembro de 2017)

merge: adicionar opção de configuração para verifySignatures

git merge --verify-signatures pode ser usado para verificar se o commit de ponta do branch que está sendo mesclado está devidamente assinado, mas é complicado ter que especificar isso todas as vezes.

Adicione uma opção de configuração que habilite esse comportamento por padrão, que pode ser substituída por --no-verify-signatures.

A git mergepágina de manual de configuração agora diz:

merge.verifySignatures:

Se verdadeiro, isso é equivalente à --verify-signaturesopção de linha de comando.


Git 2.19 (Q3 2018) é ainda mais útil, uma vez que " git verify-tag" e " git verify-commit" foram ensinados a usar o status de saída de " gpg --verify" subjacente para sinalizar assinatura inválida ou não confiável que encontraram.

Observação: com Git 2.19, gpg.formatisso pode ser definido como " openpgp" ou " x509", e gpg.<format>.programé usado para especificar qual programa usar para lidar com o formato) para permitir que x.509 certs com CMS via " gpgsm" sejam usados ​​em vez de openpgpvia " gnupg".

Consulte o commit 4e5dc9c (09 de agosto de 2018) de Junio ​​C Hamano ( gitster) .
Auxiliado por: Vojtech Myslivec ( VojtechMyslivec) , brian m. carlson ( bk2204) e Jeff King ( peff) .
(Incorporado por Junio ​​C Hamano - gitster- no commit 4d34122 , 20 de agosto de 2018)

gpg-interface: propaga o status de saída de gpg volta para os chamadores

Quando a API gpg-interface unificou o suporte para codepaths de verificação de assinatura para tags assinadas e commits assinados em meados de 2015 por volta das v2.6.0-rc0 ~ 114, acidentalmente perdemos a verificação de assinatura GPG.

Antes dessa mudança, os commits assinados eram verificados procurando por " G" assinatura do GPG, ignorando o status de saída do gpg --verifyprocesso " ", enquanto as tags assinadas eram verificadas simplesmente passando o status de saída de "gpg --verify"por meio.

O código unificado que temos atualmente ignora o status de saída de " gpg --verify" e retorna a verificação bem-sucedida quando a assinatura corresponde a uma chave não expirada, independentemente da confiança colocada na chave (ou seja, além de " G" outras, aceitamos as " U" não confiáveis).

Faça com que esses comandos sinalizem falha com seu status de saída quando o " gpg --verify" subjacente (ou o comando personalizado especificado pela gpg.programvariável de configuração " ") o fizer.
Isso essencialmente muda seu comportamento de uma maneira incompatível com versões anteriores para rejeitar assinaturas que foram feitas com chaves não confiáveis, mesmo se forem verificadas corretamente, pois é assim que " gpg --verify" se comporta.

Observe que o código ainda substitui um status de saída zero obtido de " gpg" (ou gpg.program) se a saída não disser que a assinatura é boa ou computar corretamente, mas feita com chaves não confiáveis, para capturar um invólucro mal escrito em torno " gpg" que o usuário pode nos fornecer .

Poderíamos excluir o Usuporte " " confiável deste código de fallback, mas isso seria fazer duas alterações incompatíveis com versões anteriores em um único commit, então vamos evitar isso por enquanto.
Uma mudança de acompanhamento poderia fazer isso, se desejado.


a chave é necessária para ser confiável / assinada antes de fazer qualquer criptografia

No lado da confiança, há progresso:
com o Git 2.26 (Q1 2020), a gpg.minTrustLevelvariável de configuração foi introduzida para informar a vários codepaths de verificação de assinatura o nível mínimo de confiança necessário.

Veja o commit 54887b4 (27 de dezembro de 2019) de Hans Jerry Illikainen ( illikainen) .
(Fundido por Junio ​​C Hamano - gitster- no commit 11ad30b , 30 de janeiro de 2020)

gpg-interface: adicione minTrustLevel como uma opção de configuração

Assinado por: Hans Jerry Illikainen

Anteriormente, a verificação de assinatura para operações de mesclagem e pull verificava se a chave tinha um nível de confiança de TRUST_NEVERou TRUST_UNDEFINEDem verify_merge_signature().

Se for esse o caso, o processo die()'d.

Os outros caminhos de código que faziam a verificação de assinatura dependiam inteiramente do código de retorno de check_commit_signature().

E as assinaturas feitas com uma boa chave, independentemente do seu nível de confiança, foram consideradas válidas por check_commit_signature().

Essa diferença de comportamento pode induzir os usuários a supor erroneamente que o nível de confiança de uma chave em seu chaveiro é sempre considerado pelo Git, mesmo para operações onde não é (por exemplo, durante a verify-commitou verify-tag) .

Funcionava gpg-interface.carmazenando o resultado do status da chave / assinatura e os dois níveis de confiança mais baixos no resultmembro da signature_checkestrutura (a última dessas linhas de status que foram encontradas foi gravada emresult ).

Estes são documentados no GPG na subseção General status codeseKey related , respectivamente.

A documentação GPG diz o seguinte sobre os TRUST_ statuscódigos :


Estes são vários códigos de status semelhantes:

- TRUST_UNDEFINED <error_token>
- TRUST_NEVER     <error_token>
- TRUST_MARGINAL  [0  [<validation_model>]]
- TRUST_FULLY     [0  [<validation_model>]]
- TRUST_ULTIMATE  [0  [<validation_model>]]

Para boas assinaturas, uma dessas linhas de status é emitida para indicar a validade da chave usada para criar a assinatura.
Os valores de token de erro atualmente são emitidos apenas pelo gpgsm.


Minha interpretação é que o nível de confiança é conceitualmente diferente da validade da chave e / ou assinatura.

Essa também parece ter sido a suposição do código antigo em check_signature()que um resultado de ' G' (como em GOODSIG) e ' U' (como em TRUST_NEVERouTRUST_UNDEFINED) ambos foram considerados um sucesso.

Os dois casos em que um resultado da ' U' tinha um significado especial estavam em verify_merge_signature()(onde isso causou gita die()) e format_commit_one()(onde isso afetou a saída do%G? especificador de formato).

Acho que faz sentido refatorar o processamento de TRUST_ statuslinhas de modo que os usuários possam configurar um nível mínimo de confiança que é imposto globalmente, em vez de ter partes individuais degit (por exemplo, mesclagem) façam isso sozinhos (exceto por um período de tolerância com compatibilidade com versões anteriores).

Eu também acho que faz sentido não armazenar o nível de confiança no mesmo membro da estrutura que o status da chave / assinatura.

Embora a presença de um TRUST_ statuscódigo implique que a assinatura é boa (veja o primeiro parágrafo no trecho incluído acima), até onde eu posso dizer, a ordem das linhas de status do GPG não está bem definida; portanto, parece plausível que o nível de confiança possa ser sobrescrito com o status de chave / assinatura se eles estiverem armazenados no mesmo membro da signature_checkestrutura.

Este patch introduz uma nova opção de configuração: gpg.minTrustLevel.

Ele consolida a verificação de nível de confiança gpg-interface.ce adiciona um novo trust_levelmembro à signature_checkestrutura.

A compatibilidade com versões anteriores é mantida pela introdução de um caso especial em verify_merge_signature()que, se nenhum configurável pelo usuário gpg.minTrustLevelfor definido, o antigo comportamento de rejeição TRUST_UNDEFINEDeTRUST_NEVER é aplicada.

Se, por outro lado, gpg.minTrustLevelfor definido, esse valor substituirá o comportamento antigo.

Da mesma forma, o %G?especificador de formato continuará mostrando ' U' para assinaturas feitas com uma chave que tenha um nível de confiança de TRUST_UNDEFINEDou TRUST_NEVER,mesmo que o Ucaractere ' ' não exista mais no resultmembro da signature_checkestrutura.

Um novo especificador de formato, %GT também é introduzido para usuários que desejam mostrar todos os níveis de confiança possíveis para uma assinatura.

Outra abordagem teria sido simplesmente abandonar o requisito de nível de confiança em verify_merge_signature() .

Isso também tornaria o comportamento consistente com outras partes do git que executam a verificação de assinatura.

No entanto, exigir um nível mínimo de confiança para a assinatura de chaves parece ter um caso de uso real.

Por exemplo, o sistema de compilação usado pelo projeto Qubes OS atualmente analisa a saída bruta de verify-tag para afirmar um nível mínimo de confiança para as chaves usadas para assinar tags git .

A git config gpgpágina do manual agora inclui:

gpg.minTrustLevel:

Especifica um nível mínimo de confiança para verificação de assinatura.
Se esta opção não estiver definida, a verificação de assinatura para operações de mesclagem requer uma chave com pelo menos marginalconfiança.
Outras operações que realizam verificação de assinatura requerem uma chave com pelo menos undefinedconfiança.
Definir esta opção substitui o nível de confiança necessário para todas as operações. Valores suportados, em ordem crescente de significância:

  • undefined
  • never
  • marginal
  • fully
  • ultimate

Com o Git 2.26 (Q1 2020) , " git show" e outros forneceram um nome de objeto em formato bruto em sua saída de erro, que foi corrigido para apresentá-lo em hexadecimal.

show_one_mergetag: imprime não pai em formato hexadecimal.

Quando um mergetag nomeia um não-pai, o que pode ocorrer após um clone superficial, seu hash foi impresso anteriormente como dados brutos.
Em vez disso, imprima em formato hexadecimal.

Testado com git -C shallow log --graph --show-signature -n1 plain-shallowapós umgit clone --depth 1 --no-local . shallow


Com o Git 2.27 (Q2 2020), o código para fazer a interface com o GnuPG foi refatorado.

Ver commit 6794898 , commit f1e3df3 (04 Mar 2020) de Hans Jerry Illikainen ( illikainen) .
(Fundido por Junio ​​C Hamano - gitster- no commit fa82be9 , 27 de março de 2020)

gpg-interface: prefira check_signature()para verificação GPG

Assinado por: Hans Jerry Illikainen

Este commit refatora o uso de verify_signed_buffer()fora de gpg-interface.cpara usar em seu check_signature()lugar.

Ela também se transforma verify_signed_buffer()em uma função local de arquivo, pois agora é invocada apenas internamente por check_signature().

Anteriormente, havia duas funções com escopo global usadas em diferentes partes do Git para realizar a verificação de assinatura GPG: verify_signed_buffer()e check_signature().

Agora apenas check_signature()é usado.

A verify_signed_buffer()função não protege contra assinaturas duplicadas, conforme descrito por Michał Górny .

Em vez disso, ele apenas garante um código de saída não errôneo do GPG e a presença de pelo menos um GOODSIGcampo de status.

Isso contrasta com o check_signature()que retorna um erro se mais de uma assinatura for encontrada.

O menor grau de verificação torna o uso verify_signed_buffer()problemático se os chamadores não analisam e validam as várias partes da mensagem de status GPG.

E processar essas mensagens parece uma tarefa que deve ser reservada gpg-interface.ccom a função check_signature().

Além disso, o uso de verify_signed_buffer()torna difícil introduzir novas funcionalidades que dependem do conteúdo das linhas de status do GPG.

Agora, todas as operações que fazem verificação de assinatura compartilham um único ponto de entrada para gpg-interface.c.

Isso torna mais fácil propagar funcionalidade alterada ou adicional na verificação de assinatura GPG para todas as partes do Git, sem ter casos extremos estranhos que não realizam o mesmo grau de verificação .

VonC
fonte
4

Uma inspeção superficial do código sugere que esse método direto não existe.

Todos os testes no código-fonte git contam com o grepping da saída de git show(consulte t / t7510-signed-commit.sh para os testes).

Você pode personalizar a saída usando algo parecido --pretty "%H %G?%"para torná-la fácil de analisar.

Parece que você pode pedir git mergepara verificar uma assinatura, mas, novamente, seus testes dependem grep(consulte t / t7612-merge-verify-signatures.sh ). Parece que uma assinatura inválida fará com git mergeque saia com uma assinatura incorreta, então você pode potencialmente hackear isso fazendo um teste de mesclagem em algum lugar e descartando essa mesclagem, mas isso parece pior do que apenas chamar grep.

Emil Sente-se
fonte