U.S. Cyber Command Valentine’s Day 2021 Crypto Challenge (Puzzle 7)

Pour la Saint Valentin 2021, l'U.S. Cyber Command propose dans un tweet de résoudre une douzaine de puzzles de cryptographie. Cliquez ici pour les récupérer. Une excellente initiative qui suscitera peut-être des vocations...
Pourquoi ne pas s'y essayer ? Nous verrons bien où cela nous mène... Je publierai sur ce site les solutions auxquelles je serai parvenu, sous la forme des notes prises chemin faisant, rendant donc compte d'éventuels errements, ce qui sera plus vivant.
Aujourd'hui, la solution du puzzle 7.
L'image est un BMP de 1 228 octets. Elle fait 20 x 20 = 400 pixels. Comme 20 x 20 x 3 = 1 200 octets, pas de raison de penser que le BMP contienne des données superflues qui ne sont pas affichées.
Voyons voir les bits les moins significatifs (LSB) :
from PIL import Image
import numpy as np

def getPixels (path, filename, show=False, verbose=False):
	img = Image.open (f'{path}{filename}')
	if show:
		plt.imshow (img)
		plt.show ()
	image = {}
	image['width'] = img.width
	image['height'] = img.height
	image['pixels'] = np.array (img, dtype=np.uint8)
	if verbose:
		print (f'{filename} loaded: {img.width} x {img.height} pixels')
	return image

def dumpLSBImages (path, filename, bits=(0, 0, 0), gradient=True, show=False, dump=True):
	bitsToTest = 0
	for i in range (3):
		if bits[i] != -1:
			bitsToTest += 1
	image = getPixels (path, filename, False)
	bitplane = np.zeros ((image['height'], image['width'], 3) ,dtype=np.uint8)
	tone = 255
	for y in range (image['height']):
		for x in range (image['width']):
			bitsTo1 = 0
			for i in range (len (bits)):
				if bits[i] == -1:
					continue
				if image['pixels'][y][x][i] & (1 << bits[i]):
					bitsTo1 += 1
			if bitsTo1 == 0:
				continue
			if gradient:
				tone = round (255 * bitsTo1 / bitsToTest)
			bitplane[y][x] = [tone] * 3
	bitplane = Image.fromarray (bitplane)
	if show:
		plt.imshow (bitplane)
		plt.show ()
	if dump:
		flags = ''
		for i in range (3):
			if bits[i] == -1:
				flags += '-'
			else:
				flags += str (bits[i])
		bitplane.save (f'{path}{filename[0:len (filename) - 4]} ({flags}).png')
		print (f'Dumped {path}{filename[0:len (filename) - 4]} ({flags}).png')

path = '../puzzle-7/'
filename = 'puzzle-7.bmp'
dumpLSBImages (path, filename, (0, -1, -1), False, False, True)
dumpLSBImages (path, filename, (-1, 0, -1), False, False, True)
dumpLSBImages (path, filename, (-1, -1, 0), False, False, True)
Clairement, l'image du G est suspecte :
J'extrais les octets du G :
def dumpBytes (path, filename, data, verbose=False):
	file = open (f'{path}{filename}', 'wb')
	file.write (data)
	file.close ()
	if verbose:
		print (f'{len (data)} bytes dumped in {path}{filename}')

path = '../puzzle-7/'
filename = 'puzzle-7.bmp'
image = getPixels (path, f'{filename}', False)
data = bytearray ()
for y in range (image['height']):
	for x in range (image['width']):
		data.append (image['pixels'][y][x][1])
dumpBytes (path, f'{filename[:-4]} (-G-).bin', data, True)
Cela me donne quelque chose ! Les retours à la ligne sont des 0x0a :
####################me? [13 char]
###### is this person's na#############
ANSWERdecrypting it
######ate length and then  text of an appropri random alphanumericessays by generatingrites his books and its

[This person] wpted a box of AlphaBs person] once decryssing his eyes

[Thi to a mirror and croby holding a hash up SHA-256 collisions This person] can see##################
[SOLUTION RIDDLE : 
#
En fait, il faut extraire les lignes dans le sens inverse :
path = '../puzzle-7/'
filename = 'puzzle-7.bmp'
image = getPixels (path, f'{filename}', False)
data = bytearray ()
for y in range (image['height'] - 1, -1, -1):
	for x in range (image['width']):
		data.append (image['pixels'][y][x][1])
dumpBytes (path, f'{filename[:-4]} (-G-).bin', data, True)
Ce qui me donne :
SOLUTION RIDDLE : 
###################
[This person] can see SHA-256 collisions by holding a hash up to a mirror and crossing his eyes

[This person] once decrypted a box of AlphaBits

[This person] writes his books and essays by generating random alphanumeric text of an appropriate length and then decrypting it
###################
ANSWER is this person's name? [13 char]
##########################
La solution de l'énigme fait 13 caractères, mais le flag ferait 14 caractères et 2 mots ?
L'énigme ne me parle pas. D'après la description de la personne, je présume qu'il s'agit d'un cryptographe moderne...
Je me rends sur Amazon, et je cherche les auteurs de livres de cryptographie. Je vois que cela risque d'être fastidieux... Je cherche une liste de cryptographes, et je tombe sur cette page de Wikipédia.
C'est bien simple, je copie-colle tous les noms des crytographes modernes dans Excel, j'écris une formule pour me générer les liens vers l'éventuel PNG, et je teste :
C'est ainsi que je tombe sur Bruce Schneier, et donc sur cette image :
Au suivant !
U.S. Cyber Command Valentine’s Day 2021 Crypto Challenge (Puzzle 7)