Hi,
in the past I found out that the AEAD EAX releases unauthenticated plaintext [1]. Since then, GnuPG was modified to use OCB. I have done a basic bit-flipping test on OCB, and while gcry_cipher_checktag reports a checksum error, it does release unauthenticated plaintext, and also reports a DECRYPTION_OK/GOODMDC status line.
[1] https://mailarchive.ietf.org/arch/msg/openpgp/tN2jWx4hUtiMGSo8-ILRXYNumVY/
I am including a docker file to build GnuPG and generate a test key, as well as a test script to illustrate the problem. When I run this, I get:
# python3 test-aead.py
gpg: NOTE: THIS IS A DEVELOPMENT VERSION!
gpg: It is only intended for test purposes and should NOT be
gpg: used in a production environment or with production keys!
Bit flipped at byte 109863 bit 4
gpg: NOTE: THIS IS A DEVELOPMENT VERSION!
gpg: It is only intended for test purposes and should NOT be
gpg: used in a production environment or with production keys!
[GNUPG:] ENC_TO C449D89F69F97CF9 18 0
[GNUPG:] KEY_CONSIDERED F837EEC1E3043DEA0C6BDA1B5F6F2B595BFD6E2B 0
gpg: encrypted with cv25519 key, ID C449D89F69F97CF9, created 2024-03-13
"aeadtest"
[GNUPG:] KEY_CONSIDERED F837EEC1E3043DEA0C6BDA1B5F6F2B595BFD6E2B 0
[GNUPG:] KEY_CONSIDERED F837EEC1E3043DEA0C6BDA1B5F6F2B595BFD6E2B 0
[GNUPG:] DECRYPTION_KEY A1E7C406ED9548E816174D68C449D89F69F97CF9 F837EEC1E3043DEA0C6BDA1B5F6F2B595BFD6E2B u
[GNUPG:] BEGIN_DECRYPTION
[GNUPG:] DECRYPTION_INFO 0 9 2
[GNUPG:] PLAINTEXT 62 1710341062 declaration.txt
[GNUPG:] PLAINTEXT_LENGTH 273600
gpg: gcry_cipher_checktag failed: Checksum error
[GNUPG:] DECRYPTION_OKAY
[GNUPG:] GOODMDC
[GNUPG:] END_DECRYPTION
File differs from byte 109702 to byte 109717
File 1: n and conscience
File 2: \xb7\xdc\xd6\xd1\xd5\xc9\xf2\xfa_\x8b!\xc4T\x94
File differs from byte 272950 to byte 273599
File 1: equal in dignity and rights. They are endowed with reason and conscience and should act towards one another in a spirit of brotherhood.
All human beings are born free and equal in dignity and rights. They are endowed with reason and conscience and should act towards one another in a spirit of brotherhood.
All human beings are born free and equal in dignity and rights. They are endowed with reason and conscience and should act towards one another in a spirit of brotherhood.
All human beings are born free and equal in dignity and rights. They are endowed with reason and conscience and should act towards one another in a spirit of brotherhood.
File 2: \x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00The source code for the test program:
import random
import sys
import os
# Write the first line of the declaration of human rights a lot of times to a file.
def write_file(filename):
with open(filename, 'w') as f:
for i in range(1600):
f.write('All human beings are born free and equal in dignity and rights. They are endowed with reason and conscience and should act towards one another in a spirit of brotherhood.\n')
def encrypt_file(filename, recipient):
os.system('gpg --compress-algo=0 --encrypt --recipient ' + recipient + ' ' + filename)
return filename + '.gpg'
# Read a file, flip a random bit in it, and write the result back to a file.
def flip_bit(filename):
with open(filename, 'rb') as f:
data = bytearray(f.read())
bit = random.randint(0, 8 * len(data) - 1)
byte, bit = divmod(bit, 8)
data[byte] ^= 1 << bit
with open(filename, 'wb') as f:
f.write(data)
print("Bit flipped at byte", byte, "bit", bit)
def decrypt_file(filename):
os.system('gpg --status-fd=2 --output ' + filename[:-4] + ".out" + ' --decrypt ' + filename)
return filename[:-4] + ".out"
def printable_bytes(data):
return data.replace(b'\x00', b'\\x00').decode('utf-8', 'backslashreplace')
def compare_files(file1, file2):
with open(file1, 'rb') as f1, open(file2, 'rb') as f2:
data1 = f1.read()
data2 = f2.read()
differing_bytes = []
for i in range(len(data1)):
if data1[i] != data2[i]:
differing_bytes.append(i)
# Summarise differing ranges.
i = 0
while i < len(differing_bytes):
start = differing_bytes[i]
while i < len(differing_bytes) - 1 and differing_bytes[i] + 1 == differing_bytes[i + 1]:
i += 1
end = differing_bytes[i]
if start == end:
print("File differs at byte", start)
# Print the differing bytes in a human-readable format, using \xXX if necessary. Special care needs to be taken to represent zero bytes
else:
print("File differs from byte", start, "to byte", end)
print("File 1:", printable_bytes(data1[start:end + 1]))
print("File 2:", printable_bytes(data2[start:end + 1]))
i += 1
if not differing_bytes:
print("Files are the same")
def main():
filename = 'declaration.txt'
recipient = 'aeadtest'
write_file(filename)
encrypted_file = encrypt_file(filename, recipient)
flip_bit(encrypted_file)
decrypted_file = decrypt_file(encrypted_file)
compare_files(filename, decrypted_file)
main()The docker file:
FROM ubuntu
RUN apt-get update && apt-get install -y build-essential git wget \
libldap2-dev zlib1g-dev libbz2-dev libsqlite3-dev libreadline-dev \
pcscd file ca-certificates bzip2 texinfo bison xfig imagemagick \
autogen autoconf gettext pinentry-tty && apt-get clean
RUN git clone git://git.gnupg.org/gnupg.git
RUN git clone git://git.gnupg.org/libgpg-error.git
RUN git clone git://git.gnupg.org/libgcrypt.git
RUN git clone git://git.gnupg.org/libksba.git
RUN git clone git://git.gnupg.org/npth.git
RUN git clone git://git.gnupg.org/gpgme.git
RUN git clone git://git.gnupg.org/libassuan.git
RUN git clone git://git.gnupg.org/ntbtls.git
# https://dev.gnupg.org/T4280
RUN rm /etc/ImageMagick-6/policy.xml
# Optionally pass some flags to gnupg configure by adding: speedo_pkg_gnupg_configure=
RUN (cd gnupg; ./autogen.sh && make -f build-aux/speedo.mk git-native gitrep=/)
# This would be for release build.
#RUN wget -c https://gnupg.org/ftp/gcrypt/gnupg/gnupg-2.2.11.tar.bz2 && tar xjf gnupg-2.2.11.tar.bz2
#RUN (cd gnupg-2.2.11; ./autogen.sh && make -f build-aux/speedo.mk native gitrep=git://git.gnupg.org)
ENV PATH="/gnupg/PLAY/inst/bin:${PATH}"
ENV LD_LIBRARY_PATH="/gnupg/PLAY/inst/lib"
# Example for AEAD
RUN gpg --batch --passphrase "" --quick-generate-key aeadtest
RUN echo hallo | gpg -r aeadtest -eav | gpg -d