Для решения нужно достать ключи из hex-строки. Для этого нужно декодировать ее в байты, а затем закодировать в base64.

72bca9b68fc16ac7beeb8f849dca1d8a783e8acf9679bf9269f7bf

Немного теории

Для начала разберем, что от нас требуется.

base64 — это способ кодирования информации, в котором для кодирования используется 64 бита. Данные разбиваются на блоки по 3 октета. Далее кодируемая последовательность разбивается по 6 бит и сопоставляет символ из таблицы с заданным значением. Если доступного символа нет, то вместо него подставлеется =.

Мне нравится следующая цитата:

Чего не могу создать, того не понимаю.
— Ричард Фейнман (но это не точно)

Поэтому помучаемся и напишем свою реализацию один раз.

Пишем свой base64

Не стоит серьезно воспринимать этот код, а тем более использовать его где-то в проде. Этот код написан для того, чтобы новички могли лучше понять принцип работы base64.

Кодирование

Алгоритм кодирование у нас следующий:

  1. Переводим строку в двоичный вид;
  2. Разбиваем входную строку на последовательности из 24 бит. Если не хватает символов, то добавляем 8/16 нулей, а в закодированный текст добавляется 1/2 символа = соответственно;
  3. Разбиваем на группы по 6 бит;
  4. Кодируем по таблице и добавляем =, если дополняли нулями;
Таблица кодирования base64
Decimal Binary Base64 Symbol
0000000A
1000001B
2000010C
3000011D
4000100E
5000101F
6000110G
7000111H
8001000I
9001001J
10001010K
11001011L
12001100M
13001101N
14001110O
15001111P
16010000Q
17010001R
18010010S
19010011T
20010100U
21010101V
22010110W
23010111X
24011000Y
25011001Z
26011010a
27011011b
28011100c
29011101d
30011110e
31011111f
32100000g
33100001h
34100010i
35100011j
36100100k
37100101l
38100110m
39100111n
40101000o
41101001p
42101010q
43101011r
44101100s
45101101t
46101110u
47101111v
48110000w
49110001x
50110010y
51110011z
521101000
531101011
541101102
551101113
561110004
571110015
581110106
591110117
601111008
611111019
62111110+
63111111/

Пример

IMG

Проверим:

cu63:base64/ $ echo 'QUJDREU=' | base64 -d
ABCDE

Реализация на Python:

def base64_encode(s: bytes) -> str:
    encoded_s = []
    adjust = (3 - (len(s) % 3)) % 3

    for i in range(0, len(s), 3):
        octets = s[i:i+3]
        octets = ''.join([f'{octet:08b}' for octet in octets])

        octets = octets.ljust(24, '0')
        print(octets, len(octets))
        for j in range(0, 24, 6):
            c = octets[j:j+6]
            pos = int(c, 2)
            encoded_s.append(encode_table[pos])

    if adjust != 0:
        encoded_s[-adjust::] = '=' * adjust

    return ''.join(encoded_s)

Протестирую его с ABCDE:

cu63:base64/$ python decrypt.py
QUJDREU=

Отлично. Перейдем к декодированию)

Декодирование

Логично предположить, что у нас теперь происходит обратный процесс:

  1. Разбиваем строку base64 на символы;
  2. Заменяем каждый символ 6 битами из таблицы кодирования. Считаем знаки = и исключаем их из декодированного текста;
  3. Полученные биты объединяем по 8, чтобы получить ascii символы;
  4. Отображаем полученные символы;

IMG

Реализация на Python:

def base64_decode(s: str) -> bytes:
    adjust = s.count('=')
    bin_repr = []
    decoded_str = []

    for c in s:
        if c == '=':
            bin_repr.append('0'*6)
            continue
        pos = encode_table.index(c)
        bin_repr.append(f'{pos:06b}')
    bin_repr = ''.join(bin_repr)

    if adjust != 0:
        bin_repr = bin_repr[:-adjust * 8]

    for i in range(0, len(bin_repr), 8):
        c = bin_repr[i:i+8]
        decoded_str.append(chr(int(c, 2)))

    return ''.join(decoded_str).encode('utf-8')

Итоговый код

encode_table = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'


def base64_decode(s: str) -> bytes:
    adjust = s.count('=')
    bin_repr = []
    decoded_str = []

    for c in s:
        if c == '=':
            bin_repr.append('0'*6)
            continue
        pos = encode_table.index(c)
        bin_repr.append(f'{pos:06b}')
    bin_repr = ''.join(bin_repr)

    if adjust != 0:
        bin_repr = bin_repr[:-adjust * 8]

    for i in range(0, len(bin_repr), 8):
        c = bin_repr[i:i+8]
        decoded_str.append(chr(int(c, 2)))

    return ''.join(decoded_str).encode('utf-8')


def base64_encode(s: bytes) -> str:
    encoded_s = []Ы
    adjust = (3 - (len(s) % 3)) % 3

    for i in range(0, len(s), 3):
        octets = s[i:i+3]
        octets = ''.join([f'{octet:08b}' for octet in octets])

        octets = octets.ljust(24, '0')
        print(octets, len(octets))
        for j in range(0, 24, 6):
            c = octets[j:j+6]
            pos = int(c, 2)
            encoded_s.append(encode_table[pos])

    if adjust != 0:
        encoded_s[-adjust::] = '=' * adjust

    return ''.join(encoded_s)


if __name__ == '__main__':
    input_s = 'ABCDE'
    input_s = input_s.encode('utf-8')
    crypted_str = base64_encode(input_s)
    print(crypted_str)
    decrypted_str = base64_decode(crypted_str)
    print(decrypted_str)

Наконец решим таску

Поигрались и хватит. С алгоритмом мы разобрались (разобрались же?). Можно использовать эту реализацию, либо использовать готовые либы и тулы. Теперь уже точно можно)

Я воспользуюсь двумя способами.

1) Готовая либа:

import base64


if __name__ == '__main__':
    hex_s = '72bca9b68fc16ac7beeb8f849dca1d8a783e8acf9679bf9269f7bf'
    flag = bytes.fromhex(hex_s)
    print(base64.b64encode(flag).decode())

2) Мой шедевр (ну вы его уже видели)

Флаг:

crypto/Base+64+Encoding+is+Web+Safe/