Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Signing AppImages #238

Closed
chrisnharvey opened this issue Sep 15, 2016 · 40 comments
Closed

Signing AppImages #238

chrisnharvey opened this issue Sep 15, 2016 · 40 comments

Comments

@chrisnharvey
Copy link
Contributor

Quick question. Is there any way to sign AppImages and store the signature inside the AppImage itself, similar to how Mac apps have their code signature info stored inside the application bundle?

@probonopd
Copy link
Member

probonopd commented Sep 15, 2016

No, the .asc file is external to the AppImage for now. Ideas on how to improve this are welcome.

@chrisnharvey
Copy link
Contributor Author

Would there be any way to embed this into an AppImage in the future?

I really like the idea of 1 app = 1 file. It would be great if this could remain true and have the signature inside.

@probonopd
Copy link
Member

probonopd commented Sep 15, 2016

Agree. Ideas on how to improve this are welcome. How does debian do it?

@PHPJosh
Copy link
Contributor

PHPJosh commented Sep 15, 2016

@probonopd debain doesn't store key files with packages either. they are just signed with the packagers keys and the Repos Keys and the keys have to be in the system Keyring with the rest of the trusted keys. almost every Linux distro does it this way. What could be done I guess is have a script or something (the desktopintergration?) go off and ask the users if they want to import the key into the keyring if they trust the Developer/Packager.

@chrisnharvey
Copy link
Contributor Author

According to this, three files within the deb pacakge (control.tar.gz, data.tar.gz and debian-binary) are signed, then this signature is stored within the deb package.

This is kind-of how I imagined it could work with AppImage's, but we would have to sign every file inside the AppImage, then store the signature inside the AppImage.

I thought of creating an AppImage, signing it, then storing this AppImage and its signature inside another AppImage, but I think this would be a terrible way to achieve this.

Any thoughts?

@PHPJosh
Copy link
Contributor

PHPJosh commented Sep 16, 2016

yes but the actual key file isn't stored in the deb files at all and is why the keys have to be in the systems keyring. the keys are stored on gpg key servers most the time and then are imported from said key servers into the keyring

@PHPJosh
Copy link
Contributor

PHPJosh commented Sep 16, 2016

I think best option would be to store the keys on key servers and then sign the Appimage with the keys and come up with some way to import the keys into the keyring and then have some way to verify the key and make sure the Appimage is from a trusted source

@PHPJosh
Copy link
Contributor

PHPJosh commented Sep 16, 2016

you know I just realized me and you both are developing an OS centered around Appimages.. maybe we should chit chat sometime :)

@chrisnharvey
Copy link
Contributor Author

Of course, the key would have to be stored in the users keyring, but the signature could be stored in the AppImage somehow, then this signature would be verified against the users keyring to see if its trusted.

@chrisnharvey
Copy link
Contributor Author

@PHPJosh Funnily enough, I just realised the exact same thing. We should definitely have a chat sometime.

@PHPJosh
Copy link
Contributor

PHPJosh commented Sep 16, 2016

and we are both using Arch as a Base.. think difference is I am forking Arch as I don't want rolling updates in my Project as with rolling updates things can break as since I have been working on my project i have already had some things break. will provide security and bug fix updates and Hardware updates for my project in between major versions.

well as far as talking guess we could use skype TS3 or something.. i have a ts3 server i can bring back online anytime as its been offline for months atm lol

@probonopd
Copy link
Member

probonopd commented Sep 16, 2016

So the question is: Say we generate a signature for an AppImage. Now we put the signature "into" the AppImage (into a location that was 0x00 padding before). Now, the file is no longer the same and the signature will no longer match the file.

How can we work around that? One idea is that the code that checks the signature first copies out the embedded signature from the AppImage, then overwrites the signature inside the AppImage with the original padding (0x00), verifies the signature (it should now match again!) and finally puts back the signature into the AppImage.

But that is not very elegant, there must be a better way... any ideas?

@Chaz6 suggested piping into gpg. That might work, using a tiny helper in C that replaces a given range of bytes with an equal amount of 0x00s.

http://hick.org/code/skape/papers/elfsign.txt suggests:

       1) Calculate the MD5 checksum of the binary, skipping over
          the signature section of the elf binary.
       2) Extract the signature stored in the signature section
          and determine if the binary was signed and issued
          by a trusted authority.
       3) Decrypt the signed checksum in the binary and compare it
          with your calculated checksum.  If they match the binary
          has not been modified.

So not sign the ELF but a md5 "digest" of it.

@probonopd
Copy link
Member

probonopd commented Sep 16, 2016

There seem to be ready solutions:

@probonopd
Copy link
Member

probonopd commented Sep 16, 2016

signelf by @rhvgoyal from https://lkml.org/lkml/2013/1/15/610

patch -p0 < ../signelf.diff

sudo apt-get install libssl-dev
make
sudo make install

# Make a key
openssl req -new -nodes -utf8 -sha256 -days 36500 -batch -x509  -outform DER -out new_signing_key.x509 -keyout new_signing_key.priv

# Sign itself
signelf -i signelf -o signelf.signed -p new_signing_key.priv -c new_signing_key.x509

Seems to have signed the binary BUT when I tried to sign an AppImage (which is an ELF with some filesystem image appended to the end of the ELF) it has truncated the filesystem that is appended to the ELF. So I guess it could now be about time to change the AppImage format to embed the filesystem into the ELF rather than append to it. But that would mean that it wouldn't be possible to put together AppImages easily without using a compiler, e.g., it would break what @develar does in electron-builder.

So maybe we can wrok with @rhvgoyal need to address the above, or roll our own, possibly based on signelf (seems like it didn't go much anywhere on LKML so far but hey, why not use it as inspiration).

@probonopd
Copy link
Member

Tor uses GPG for signature verification. The advantage of GPG might be that there is a web of trust that does not come from commercial "Root CAs".

https://www.torproject.org/docs/verifying-signatures.html.en

@probonopd
Copy link
Member

probonopd commented Sep 17, 2016

This is how I imagine it using GPG, what do you think?

gpg2 --full-gen-key

###################

# Simulate an AppImage
dd if=/dev/urandom bs=1M count=2 of=data

# Let's say the section for the signature is 256 bytes large starting at offset 1024
OFFSET=1024
LEN=512

# Clear the area where the signature will go
dd if=/dev/zero bs=1 seek=$OFFSET count=$LEN conv=notrunc of=data

# Generate the digest file
dd if=data bs=1 count=$OFFSET | sha512sum -b | cut -d " " -f 1 > digest
dd if=data bs=1 skip=$((OFFSET+LEN))| sha512sum -b | cut -d " " -f 1 >> digest

# Sign the digest file
gpg2 --detach-sig digest
xxd digest.sig

# Embed the signature into the section of the AppImage
dd if=digest.sig bs=1 seek=$OFFSET of=data conv=notrunc

rm digest digest.sig

###################

# On the receiving side

# Generate the digest file
dd if=data bs=1 count=$OFFSET | sha512sum -b | cut -d " " -f 1 > digest
dd if=data bs=1 skip=$((OFFSET+LEN))| sha512sum -b | cut -d " " -f 1 >> digest

# Extract the signature from the section
dd if=data bs=1 skip=$OFFSET count=$LEN of=digest.sig

gpg2 --verify digest.sig

rm digest.sig

@chrisnharvey
Copy link
Contributor Author

@probonopd Looks great. Is that something that would work now?

It would be good if we could integrate this into AppImageAssistant.

./AppImageAssistant ./App.Appdir ./App.AppImage --sign

What do you think?

@chrisnharvey
Copy link
Contributor Author

I wonder if we could also embed the public key into the AppImage? This way, if the user does not have the public key, then we can ask the user if they want to trust AppImages from this developer? Or is this a bad idea from a security standpoint?

@probonopd
Copy link
Member

probonopd commented Sep 18, 2016

You guessed it, bad idea from a security standpoint. An attacker could just sign the package himself and sneak in his own public key.

Keyservers/web of trust is it.

@probonopd
Copy link
Member

Is that something that would work now?

No, since we don't have the ELF section for it yet. But I am working on it.

It would be good if we could integrate this into AppImageAssistant

Yes. Or a successor of it.

@PHPJosh
Copy link
Contributor

PHPJosh commented Sep 18, 2016

will if this gets integrated into Appimage I will pitch in on desktop integration . like Crisnharvey was saying we need to come up for a way for the end users to import the keys into the keyring

@PHPJosh
Copy link
Contributor

PHPJosh commented Sep 18, 2016

which i am with probonopd key servers all the way

@probonopd
Copy link
Member

What worries me a bit is that there were several attempts at signing ELF (all technically working) around 10 years ago but none seems to have sparked much interest. Well, the same can be said for application bundles... but that's seemingly changing now.

@PHPJosh
Copy link
Contributor

PHPJosh commented Sep 18, 2016

well hopfully the App bundle situation changes soon. I have known about AppImageKit for years and it has baffled me why no one before Me and Crisnharvey has build a Distro around them. and frankly IMO Snaps and Flatpaks are no good as the end result to me seems like a lot of unneeded bloat compared to AppImage

@probonopd
Copy link
Member

probonopd commented Sep 24, 2016

dd bs=1 is way too slow...

# Generate a signing key

gpg2 --full-gen-key

###################

# After AppImage generation (containing a .gpg_sig ELF segment like appimagetool produces)

SIGHEXOFFSET=$(objdump -h "${APPIMAGE}" | grep .gpg_sig | awk '{print $6}')
SIGHEXLENGTH=$(objdump -h "${APPIMAGE}" | grep .gpg_sig | awk '{print $3}')
dd bs=1 if="${APPIMAGE}" count=$(($(echo 0x$SIGHEXOFFSET)+0)) | sha256sum | cut -d " " -f 1 > /tmp/digest
dd bs=1 if="${APPIMAGE}" skip=$(($(echo 0x$SIGHEXOFFSET)+$(echo 0x$SIGHEXLENGTH))) | sha256sum | cut -d " " -f 1 >> /tmp/digest

cat /tmp/digest # sign this

# Sign the digest file
gpg2 --detach-sig /tmp/digest
xxd /tmp/digest.sig

# Embed the signature into the section of the AppImage
dd if=/tmp/digest.sig bs=1 seek=$(($(echo 0x$SIGHEXOFFSET)+0)) of="${APPIMAGE}" conv=notrunc

rm /tmp/digest /tmp/digest.sig

###################

# On the receiving side

SIGHEXOFFSET=$(objdump -h "${APPIMAGE}" | grep .gpg_sig | awk '{print $6}')
SIGHEXLENGTH=$(objdump -h "${APPIMAGE}" | grep .gpg_sig | awk '{print $3}')
dd bs=1 if="${APPIMAGE}" count=$(($(echo 0x$SIGHEXOFFSET)+0)) | sha256sum | cut -d " " -f 1 > /tmp/digest
dd bs=1 if="${APPIMAGE}" skip=$(($(echo 0x$SIGHEXOFFSET)+$(echo 0x$SIGHEXLENGTH))) | sha256sum | cut -d " " -f 1 >> /tmp/digest

# Extract the signature from the section
dd if="${APPIMAGE}" bs=1 skip=$(($(echo 0x$SIGHEXOFFSET)+0)) count=$(($(echo 0x$SIGHEXLENGTH)+0)) of=/tmp/digest.sig

gpg2 --verify /tmp/digest.sig

rm /tmp/digest /tmp/digest.sig

@probonopd
Copy link
Member

probonopd commented Sep 24, 2016

Looks like elfsign cripples ELFs:

tar xfv elfsign-0.2.2.tar.gz 
elfsign-0.2.2/
sudo apt-get install libssl-dev
sudo ln -s /lib/x86_64-linux-gnu/libcrypto.so.1.0.0 /usr/lib/libcrypto.so
./configure --with-openssl=/usr/
make
sudo make install

openssl req -new > cert.csr
openssl rsa -in privkey.pem -out key.pem
openssl x509 -in cert.csr -out cert.pem -req -signkey key.pem -days 1001
cat key.pem>>cert.pem

elfsign -f XChat_IRC-x86_64.AppImage -c cert.pem -p privkey.pem

./XChat_IRC-x86_64.AppImage 
bash: ./XChat_IRC-x86_64.AppImage: cannot execute binary file: Exec format error

Can anyone get it to work?

@probonopd
Copy link
Member

probonopd commented Sep 24, 2016

elfgpg doesn't work for me either:

git clone https://github.com/bartman/elfgpg.git
cd elfgpg/
sudo apt-get install libelf-dev libgpgme11-dev
libtoolize --force
aclocal
autoheader
automake --force-missing --add-missing
autoconf
./configure
make # errors 
cd src
sed -i -e 's|-Wall -Werror||g' ./src/Makefile
gcc -O2  -g -O2   -o elfgpg main.o sign.o verify.o dump.o elfstrings.o  -lelf -lgpgme -lgpg-error

# Results in:
# gpgme_new: Not operational
# failed to init libgpgme

Can anyone get it to work?

@PHPJosh
Copy link
Contributor

PHPJosh commented Sep 24, 2016

I will mess with it once i have some spare time ( still compiling packages) as this is very important to figure out :)

@PHPJosh
Copy link
Contributor

PHPJosh commented Sep 25, 2016

maybe by looking at the elfsign code u posted the actual elf in the appimage needs to be signed and not the resulting appimage cause the appimage is an iso

@probonopd
Copy link
Member

We want the ISO part to be signed too! Otherwise the signature is worthless...

@PHPJosh
Copy link
Contributor

PHPJosh commented Sep 25, 2016

yep i know but i think the elfsign is breaking it cause its actually an ISO with an elf on the inside. but best i can think is sign it like any other file which would produce a .sig file which couldn't we embed that in the appimage and somehow when its needs to be verified check the keys against the sig file? little read here how these guys verify ISO's http://www.mepiscommunity.org/wiki/system/signed-iso-files

@probonopd
Copy link
Member

Let's not focus on ISOs so much since AppImage type 2 format will not be ISO based anymore.

@PHPJosh
Copy link
Contributor

PHPJosh commented Sep 25, 2016

ah was the code you posted with the new squashfs format? if so should look into if squashfs can be signed. pretty sure it can be

@probonopd
Copy link
Member

The signature mechanism should be independent of the particular file format used. It should sign the entire file except for a "hole" in which the signature goes. This will be an ELF segment.

@probonopd
Copy link
Member

probonopd commented Sep 25, 2016

Writing digest.c which takes a file, an offset in bytes, and a length in bytes, and calculates the sha256 checksum over the file except the area from byte offset to byte offset+length. This is useful when a signature is placed in the skipped area. The idea is that this sha256 sum will be signed, and then the signature can be stored inside the skipped area. On the receiving end, the same sha256 sum will be generated and the signature will match.

Here is an example of how it works:

me@host:~/appimagetool/build$ echo -n "" > test 
me@host:~/appimagetool/build$ sha256sum test 
e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855  test
me@host:~/appimagetool/build$ ./digest test 0 0
totalBytesRead: 0
e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
# OK

me@host:~/appimagetool/build$ echo -n "abc" > test 
me@host:~/appimagetool/build$ sha256sum test 
ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad  test
me@host:~/appimagetool/build$ ./digest test 0 0
totalBytesRead: 3
ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad
# OK

me@host:~/appimagetool/build$ echo -n "abc123" > test
me@host:~/appimagetool/build$ ./digest test 3 3
totalBytesRead: 3
ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad
# OK

me@host:~/appimagetool/build$ echo -n "axxxbc" > test 
me@host:~/appimagetool/build$ ./digest test 1 3
totalBytesRead: 3
ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad
# OK

me@host:~/appimagetool/build$ ./digest test 0 6
totalBytesRead: 0
e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
# OK

@probonopd
Copy link
Member

Modified the behavior so that the skipped area is not actually skipped but filled with 0x00s during checksum calculation. This allows one to use the md5sum command line tool on the AppImage before injecting the signature.

Test cases change as follows:

echo -n "" > test 
sha256sum test 
e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855  test
./digest test 0 0
totalBytesRead: 0
e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
# OK

echo -n "abc" > test 
sha256sum test 
ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad  test
./digest test 0 0
totalBytesRead: 3
ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad
# OK

echo -n -e "abc\x00\x00\x00" > test
sha256sum test
dd0b251b2bf91037a1e4fc8416a24ae00bcb9a8c252dc7e2361f2fc015f51c16  test
./digest test 3 3
totalBytesRead: 6
dd0b251b2bf91037a1e4fc8416a24ae00bcb9a8c252dc7e2361f2fc015f51c16
./digest test 0 0
totalBytesRead: 6
echo -n -e "abc123" > test
./digest test 3 3
totalBytesRead: 6
dd0b251b2bf91037a1e4fc8416a24ae00bcb9a8c252dc7e2361f2fc015f51c16
# OK

echo -n -e "a\x00\x00\x00bc" > test
sha256sum test 
e858c102469e608e2ed5c013b1236f09bb6c6b0bc165b8158ef36f9e07371bbf  test
./digest test 1 3
totalBytesRead: 6
e858c102469e608e2ed5c013b1236f09bb6c6b0bc165b8158ef36f9e07371bbf
echo -n -e "a123bc" > test
./digest test 1 3
totalBytesRead: 6
e858c102469e608e2ed5c013b1236f09bb6c6b0bc165b8158ef36f9e07371bbf
# OK

./digest test 0 6
totalBytesRead: 6
b0f66adc83641586656866813fd9dd0b8ebb63796075661ba45d1aa8089e1d44
echo -n -e "\x00\x00\x00\x00\x00\x00" > test
sha256sum test 
b0f66adc83641586656866813fd9dd0b8ebb63796075661ba45d1aa8089e1d44  test
# OK

@probonopd
Copy link
Member

probonopd commented Sep 25, 2016

Digital signatures added to the AppImageSpec draft. Note that no specific format (e.g., GPG vs. X.509) has been mandated, which means that both could be used.

The following works with the new squashfs-based type 2 AppImages generated with appimagetool:

# Generate a signing key

gpg2 --full-gen-key

###################

# After AppImage generation (containing a .sha256_sig ELF segment like appimagetool produces)

# Clear the signature section of the AppImage
SIGHEXOFFSET=$(objdump -h "${APPIMAGE}" | grep .sha256_sig | awk '{print $6}')
SIGHEXLENGTH=$(objdump -h "${APPIMAGE}" | grep .sha256_sig | awk '{print $3}')
dd if=/dev/zero bs=1 seek=$(($(echo 0x$SIGHEXOFFSET))) count=$(($(echo 0x$SIGHEXLENGTH))) of="${APPIMAGE}" conv=notrunc

sha256sum "${APPIMAGE}" | cut -d " " -f 1 >> /tmp/digest

# Sign the digest file
gpg2 --detach-sign --armor /tmp/digest
cat /tmp/digest.asc

# Embed the signature into the section of the AppImage
dd if=/tmp/digest.asc bs=1 seek=$(($(echo 0x$SIGHEXOFFSET))) count=$(($(echo 0x$SIGHEXLENGTH))) of="${APPIMAGE}" conv=notrunc

rm /tmp/digest /tmp/digest.asc

###################

# On the receiving side

./digest "${APPIMAGE}" >> /tmp/digest

# Extract the signature from the section
dd if="${APPIMAGE}" bs=1 skip=$(($(echo 0x$SIGHEXOFFSET))) count=$(($(echo 0x$SIGHEXLENGTH))) of=/tmp/digest.sig

file /tmp/digest.sig | grep "PGP signature" >/dev/null 2>&1 && gpg2 --verify /tmp/digest.sig /tmp/digest

rm /tmp/digest /tmp/digest.sig

Gives:

gpg: Signature made Sun 25 Sep 2016 12:54:36 PM CEST using RSA key ID 86C3DFDD
gpg: Good signature from "Testkey" [ultimate]

@probonopd
Copy link
Member

probonopd commented Sep 25, 2016

It works! We now have signatures inside (type 2) AppImages. Try it yourself.

Generating a signed AppImage:

gpg2 --full-gen-key # Generate a signing key (once)
wget https://transfer.sh/mH0jk/appimagetool
chmod a+x ./appimagetool
./appimagetool ./XChat.AppDir --sign

Reading the signature:

./XChat_IRC-x86_64.AppImage --appimage-signature

-----BEGIN PGP SIGNATURE-----
Version: GnuPG v2

iQEcBAABCAAGBQJX6CN9AAoJENBdKWeGw9/dsvoH/RgEggMiNTwgyA4io2Dyy1j1
6U3CQST9HVmh9PjeFKZCgFCZbHvpFz9mzhLTPlOAbczBnSmmbgqROINaLW+1tqEx
stOy67D3Z1cySzRTOhSkjiUOP5unmZL6QTNPxRHmuRkyihv7YfAlkrogXQlYbZ1h
Ilt6jU1b97GSPox/EE3Z002iZGJYQ3FfjAlp9o947goY5koA5KYqyzTCvEjhTk/L
wz1mFcjEkzHt9CaHZfrZCE3QVSBTq071wzsHCFHaJswPhA6iI0psCnFY56PPResi
uljTQr3nOBaqNyUgU3y4Tbd+36cwggSaTpGAzlhgNoalIwB1ltFSdPeRPe4Q3Qc=
=MR0w
-----END PGP SIGNATURE-----

Validating the signature:

wget https://transfer.sh/UQfeE/validate
chmod a+x ./validate
./validate ./XChat_IRC-x86_64.AppImage
(...)
gpg: Signature made Sun 25 Sep 2016 10:41:24 PM CEST using RSA key ID 86C3DFDD
gpg: Good signature from "Testkey" [ultimate]

@PHPJosh
Copy link
Contributor

PHPJosh commented Sep 25, 2016

Nice! once i get my repos built (which should be next 2 days) so i can make ISO's will start messing with all this but this is going to be nice :)

@probonopd
Copy link
Member

appimagetool supports embedding signatures, and validate can check them.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants