Какие данные подписываются при `git commit --gpg-sign =<key-id>`?

Я пытаюсь выяснить, как подписывать / проверять коммиты вручную, но я не могу понять, какие данные подписываются для создания подписи. Другими словами, я не могу понять, что <data> в gpg --verify <commit-sig> <data> должно быть.

Вот соответствующий фрагмент исходного кода git: https://github.com/git/git/blob/master/commit.c#L1047-L1231 но я также новичок в C.


Вот некоторые примеры данных:

В свежем git-репо я создаю файл ledger.txt и передайте его с подписанным коммитом:

git config --global user.signingkey 7E482429
git init
echo "EAC5-531F-38E8-9670-81AE-4E77-C7AA-5FC3-7E48-2429 1\n" > ledger.txt
git add ledger.txt
git commit -m "Initial commit" --gpg-sign=7E482429

И вот это в журнале:

git log --show-signature

    commit 876793da21833b5b8197b08462523fd6aad3e5ba
    gpg: Signature made Fri May  9 20:01:55 2014 CDT using RSA key ID 7E482429
    gpg: Good signature from "Dan Neumann <danrodneu@gmail.com>"
    Author: Dan Neumann <danrodneu@gmail.com>
    Date:   Fri May 9 20:01:55 2014 -0500

        Initial commit

Вот довольно напечатанный объект коммита (который живет в .git/objects/87/6793da21833b5b8197b08462523fd6aad3e5ba):

git cat-file -p 876793da21833b5b8197b08462523fd6aad3e5ba

tree 70e7c184c3a89c749174b4987830c287fd78952d
author Dan Neumann <danrodneu@gmail.com> 1399683715 -0500
committer Dan Neumann <danrodneu@gmail.com> 1399683715 -0500
gpgsig -----BEGIN PGP SIGNATURE-----
 Version: GnuPG v1

 iQEcBAABAgAGBQJTbXqDAAoJEMeqX8N+SCQpTBIH/3zCpf0w0+xp8hkwz7dTV9Bw
 ercZp4UpxKV1HgqCxu2r/nGIuZyabLwTis1rcwXOVC4DgRxO0f2BiP0xnyL3OhJu
 CKh8l+HZvvGqVH3Dopm0D/kOxDAWHcjokbyzWBbYJX6WhvT8OI7SSYmwuF4r610h
 hkZ1xgjo4p1x9WegY296PzA1wEe6yy9BvvdIpJHoqBVKClgFrZvtE5PidbrAyLGF
 Kl/2f0K3peBdo6XP0Zaml8NyQlFmAlCV831hHgUmZsBSRpgh/WNvrDSNILTlFJgY
 BOPb2yPP+tiJOXYB66MsjQY9GlX7n43miu5wMtdk1AGqh+26OExbSrZcYVFLk4w=
 =sRee
 -----END PGP SIGNATURE-----

Initial commit

А вот фактическое содержимое файла объектного коммита:

hexdump .git/objects/87/6793da21833b5b8197b08462523fd6aad3e5ba | \
zlib-decompress | \
bin-to-ascii

commit 671\0tree 70e7c184c3a89c749174b4987830c287fd78952d\nauthor Dan Neumann <danrodneu@gmail.com> 1399683715 -0500\ncommitter Dan Neumann <danrodneu@gmail.com> 1399683715 -0500\ngpgsig -----BEGIN PGP SIGNATURE-----\n Version: GnuPG v1\n \n iQEcBAABAgAGBQJTbXqDAAoJEMeqX8N+SCQpTBIH/3zCpf0w0+xp8hkwz7dTV9Bw\n ercZp4UpxKV1HgqCxu2r/nGIuZyabLwTis1rcwXOVC4DgRxO0f2BiP0xnyL3OhJu\n CKh8l+HZvvGqVH3Dopm0D/kOxDAWHcjokbyzWBbYJX6WhvT8OI7SSYmwuF4r610h\n hkZ1xgjo4p1x9WegY296PzA1wEe6yy9BvvdIpJHoqBVKClgFrZvtE5PidbrAyLGF\n Kl/2f0K3peBdo6XP0Zaml8NyQlFmAlCV831hHgUmZsBSRpgh/WNvrDSNILTlFJgY\n BOPb2yPP+tiJOXYB66MsjQY9GlX7n43miu5wMtdk1AGqh+26OExbSrZcYVFLk4w=\n =sRee\n -----END PGP SIGNATURE-----\n\nInitial commit\n

2 ответа

Решение

После прочтения кода в commit_tree_extended кажется, что данные, используемые для подписи, являются частью от "дерева" до конца комментария, конечно, за исключением подписи.

В вашем примере это должно быть:

tree 70e7c184c3a89c749174b4987830c287fd78952d
author Dan Neumann <danrodneu@gmail.com> 1399683715 -0500
committer Dan Neumann <danrodneu@gmail.com> 1399683715 -0500
Initial commit

Из источника Git:

Инициирование буфера:

strbuf_init(&buffer, 8192); /* should avoid reallocs for the headers */
strbuf_addf(&buffer, "tree %s\n", sha1_to_hex(tree));

Родитель совершает обход:

/*
* NOTE! This ordering means that the same exact tree merged with a
* different order of parents will be a _different_ changeset even
* if everything else stays the same.
*/
while (parents) {
    struct commit_list *next = parents->next;
    struct commit *parent = parents->item;

    strbuf_addf(&buffer, "parent %s\n",
    sha1_to_hex(parent->object.sha1));
    free(parents);
    parents = next;
}

Информация о персоне / дате:

if (!author)
    author = git_author_info(IDENT_STRICT);
strbuf_addf(&buffer, "author %s\n", author);
strbuf_addf(&buffer, "committer %s\n", git_committer_info(IDENT_STRICT));
if (!encoding_is_utf8)
    strbuf_addf(&buffer, "encoding %s\n", git_commit_encoding);

while (extra) {
    add_extra_header(&buffer, extra);
    extra = extra->next;
}
strbuf_addch(&buffer, '\n');

Комментарий и проверка кодировки:

/* And add the comment */
strbuf_addbuf(&buffer, msg);

/* And check the encoding */
if (encoding_is_utf8 && !verify_utf8(&buffer))
    fprintf(stderr, commit_utf8_warn);

Вот где происходит подписание. Подпись будет добавлена ​​после заголовка.

if (sign_commit && do_sign_commit(&buffer, sign_commit))
    return -1;

Там также будет информация о родителе, если ваш коммит имел некоторые.

Этот ответ находится в стадии разработки.

Фон

Во-первых, некоторые размышления о проблемах с текущим механизмом подписи Git.

В идеале Git использовал бы один из встроенных в GnuPG механизмов подписывания. Если бы это было так, то было бы легко проверить коммиты Git без необходимости вызывать Git или писать скрипты, просто используя GnuPG gpg --verify или же gpg2 --verify,

В этом отношении очень жаль, что Git не применил механизм подписи "отсоединенной подписи" GnuPG, предложенный в списке рассылки ядра Linux в 2005 году. Совсем недавно Оуэн Джейкобсон перечислил несколько дополнительных причин, по которым отдельные подписи были бы желательны по сравнению с нынешним подходом Git. Он указывает, что в настоящее время:

  • Подписи встраиваются в объекты, которые они подписывают. Подпись является частью идентичности объекта; Так как Git имеет контент-адрес, это означает, что объект не может быть ни подписан задним числом, ни задним числом лишен своей подписи без изменения идентичности объекта. Распределенная модель Git означает, что такого рода изменения идентичности являются сложными и легко обнаруживаемыми.

  • Совершают подписи граждане второго сорта. Они являются относительно недавним дополнением к пакету Git, и как реализация, так и социальные соглашения вокруг них продолжают развиваться.

  • Только некоторые объекты могут быть подписаны. Хотя Git имеет относительно слабые правила в отношении рабочего процесса, система подписей предполагает, что вы используете один из наиболее распространенных рабочих процессов Git, ограничивая ваши параметры не более чем одной подписью и ограничивая подписи тегами и фиксациями (исключая BLOB-объекты, деревья и ссылки).).

Майк Гервитц указывает на одно из самых серьезных последствий нынешнего подхода Git. У коммитов Git есть и поле "коммиттер", и поле "автор", позволяющее коммиттеру и автору коммита быть двумя отдельными людьми. Однако в настоящее время коммиты Git позволяют включать только одну подпись. Итак, чья это подпись? В идеале и автор, и коммиттер смогут подписать коммит. Отдельные подписи позволили бы это. Таким образом, вложенные встроенные подписи, в этом отношении. Но поскольку Git не использует ни один из этих параметров, это заставляет нас выбирать между двумя неудовлетворительными параметрами:

  1. Коммиттер снимает подпись автора и подписывает коммит самостоятельно.

  2. Коммиттер отказывается подписывать коммит.

Это подводит итог плохих новостей. Хорошей новостью является то, что обертка Git для GnuPG, по крайней мере, включает в себя --show-signature вариант для git log, который проверяет подписи с помощью GnuPG. Это позволяет журналу Git показывать, была ли подпись сделана ключом с UID, которому вы доверяете.

Если это так, GnuPG покажет:

Good signature from "John Doe <john.doe@example.com>"

Если нет, GnuPG покажет:

Good signature from "John Doe <john.doe@example.com>"
WARNING: This key is not certified with a trusted signature!
         There is no indication that the signature belongs to the owner.

Ваш вопрос

Как указано в ответе Poko, ваш коммит, на первый взгляд, эквивалентен следующему документу с подтверждением:

-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

tree 70e7c184c3a89c749174b4987830c287fd78952d
author Dan Neumann <danrodneu@gmail.com> 1399683715 -0500
committer Dan Neumann <danrodneu@gmail.com> 1399683715 -0500
Initial commit

-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1

iQEcBAABAgAGBQJTbXqDAAoJEMeqX8N+SCQpTBIH/3zCpf0w0+xp8hkwz7dTV9Bw
ercZp4UpxKV1HgqCxu2r/nGIuZyabLwTis1rcwXOVC4DgRxO0f2BiP0xnyL3OhJu
CKh8l+HZvvGqVH3Dopm0D/kOxDAWHcjokbyzWBbYJX6WhvT8OI7SSYmwuF4r610h
hkZ1xgjo4p1x9WegY296PzA1wEe6yy9BvvdIpJHoqBVKClgFrZvtE5PidbrAyLGF
Kl/2f0K3peBdo6XP0Zaml8NyQlFmAlCV831hHgUmZsBSRpgh/WNvrDSNILTlFJgY
BOPb2yPP+tiJOXYB66MsjQY9GlX7n43miu5wMtdk1AGqh+26OExbSrZcYVFLk4w=
=sRee
-----END PGP SIGNATURE-----

Однако предположим, что мы сохраняем это как danneau_stackru_example.asc и попытаться проверить это, вот вывод:

$ gpg --verify danneau_stackru_example.asc 
gpg: Signature made Sat 10 May 2014 02:01:55 BST
gpg:                using RSA key C7AA5FC37E482429
gpg: Can't check signature: public key not found

Что GnuPG подразумевает под этим, так это то, что, поскольку у меня нет вашего открытого ключа, он на самом деле не может определить, хорошая подпись или плохая. Таким образом, даже если я вмешиваюсь в содержимое, я получаю тот же результат:

$ echo "-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

tree 70e7c184c3a89c749174b4987830c287fd78952d
author Dan Neumann <danrodneu@gmail.com> 1399683715 -0500
committer Dan Neumann <danrodneu@gmail.com> 1399683715 -0500

EVIL MESSAGE HERE

-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1

iQEcBAABAgAGBQJTbXqDAAoJEMeqX8N+SCQpTBIH/3zCpf0w0+xp8hkwz7dTV9Bw
ercZp4UpxKV1HgqCxu2r/nGIuZyabLwTis1rcwXOVC4DgRxO0f2BiP0xnyL3OhJu
CKh8l+HZvvGqVH3Dopm0D/kOxDAWHcjokbyzWBbYJX6WhvT8OI7SSYmwuF4r610h
hkZ1xgjo4p1x9WegY296PzA1wEe6yy9BvvdIpJHoqBVKClgFrZvtE5PidbrAyLGF
Kl/2f0K3peBdo6XP0Zaml8NyQlFmAlCV831hHgUmZsBSRpgh/WNvrDSNILTlFJgY
BOPb2yPP+tiJOXYB66MsjQY9GlX7n43miu5wMtdk1AGqh+26OExbSrZcYVFLk4w=
=sRee
-----END PGP SIGNATURE-----
" | gpg --verify -
gpg: Signature made Sat 10 May 2014 02:01:55 BST
gpg:                using RSA key C7AA5FC37E482429
gpg: Can't check signature: public key not found

Таким образом, это означает, что Поко, возможно, неправильно понял, что именно вы подписали в конце концов.

Чтобы разобраться в этом, я попытался преобразовать коммиты, подписанные закрытыми ключами, чьи соответствующие открытые ключи , которыми я владею, в очищенные файлы и передать их в GnuPG для проверки. До сих пор я только когда-либо получал ответы "Плохая подпись". Если я выясню, где я иду не так, я обновлю этот ответ.

Другие вопросы по тегам