Crypto Review (1)

yeon·1일 전
post-thumbnail

인코딩(Encoding)

인코딩이란 사람이 이해할 수 있는 형태의 정보를 컴퓨터나 시스템이 이해할 수 있는 표현법을 따르는 데이터로 변환하는 과정을 의미한다.

이 반대의 과정을 디코딩(Decoding)이라고 하고, 다양한 표현법이 있지만 여기에서는 파이썬으로 ASCII, Hex, Base64를 다루려고 한다.

암호 공부하다가 인코딩을 다루는 이유는 암호 문제에서는 같은 데이터가 여러 형태로 표현되는 일이 빈번하기 때문이다.

파이썬에서 ASCII 변환하기

아스키 코드는 문자나 기호를 7비트 숫자로 표현한다.

chr(97) # 결과:'a' : ASCII -> 문자
ord('a') # 결과:97 : 문자 -> ASCII

파이썬에서 Hex 변환하기

Hex는 그냥 16진수(0~F)이다.
한 자리가 4비트이기 때문에 두 자리는 1바이트 크기로, RGB 색상 표현에도 사용되고 데이터 값 표현이 압축된다는 장점이 있다.

bytes.fromhex("48656c6c6f") # 결과:b'Hello' : Hex -> Byte
b"Hello".hex() # 결과:48656c6c6f : Byte -> Hex

파이썬에서 Base64 변환하기

Base64는 한 자리가 6bit로, 4자리가 3바이트 크기이다.
a-z 26개, A-Z 26개, 0-9 10개에 +와 / 두 개를 더한 ASCII 문자 64개를 사용한다.
이미지나 파일 같은 binary 데이터를 텍스트 기반 환경에서 전달할 때 사용한다.

입력이 3바이트 단위로 딱 떨어지지 않으면 패딩(=)을 붙여야 된다.
= 하나는 두 바이트로만 디코딩된다는 뜻이고, == 는 한 바이트로만 디코딩된다는 뜻이다.
예를 들어 "Hello"(5바이트) 입력이라면 마지막에 = 하나가 붙는다.

import base64
base64.b64encode(b"Hello") # Byte -> Base64

파이썬에서 Hex에서 10진수로 변환하기

from Crypto.Util.number import bytes_to_long, long_to_bytes

bytes_to_long(b'Hello') # Byte -> Integer
long_to_bytes(310939249775) # Integer -> Byte

문제에서는 원래 메시지가 문자열 형태가 아니라 하나의 매우 큰 정수로 주어진다.


XOR

XOR은 암호 문제에서 단골 bitwise 연산이다.
두 bit가 같으면 0, 다르면 1이다.

문자끼리 XOR할 수는 없기 때문에 먼저 문자를 정수로 변환하는 과정이 필요하다.
예: chr(ord('A') ^ 13)

Self-inverse 성질이 있다. (자기 자신과 XOR하면 0)
예: (M ⊕ K) ⊕ K = M
이 특성은 암호화 과정에서 매우 많이 사용되기 때문에 필수로 알아둬야 한다.

유용한 함수

# 바이트로 표현된 크기가 같은 a,b를 XOR 연산하는 함수
def xor_bytes(a, b):
    return bytes(x ^ y for x, y in zip(a, b))
    
# 반복되는 key로 data와 XOR 연산하는 함수
def xor_with_repeating_key(data: bytes, key: bytes) -> bytes:
    result = []
    for i, byte in enumerate(data):
        # key의 인덱스를 돌려가며 XOR 연산
        result.append(byte ^ key[i % len(key)])
    return bytes(result)

실제로 문제풀이에서 알차게 써먹은 함수들이다.


Brute Force

가능한 key의 수가 작다면 key를 모르더라도 모든 값을 하나씩 시도하면서 찾아나갈 수 있다.

이렇게 가능한 모든 조합을 무차별적으로 시도하여 암호를 해독하는 공격을 Brute Force라고 한다.

# 1-byte key (256가지 경우의 수) 조건이라면

for key in range(256): # 가능한 Key를 전부 돌려보기
	result = bytes(b ^ key for b in ciphertext)
    
    # 이미 알고 있는 FLAG 형식으로 걸러내기
    if result.startswith(b"crypto{"):
    	print("key =", key)
        print("result =", result)

코드 블럭에서 if문을 사용한 것처럼, 실제 문제에서는 알고 있는 평문의 특징을 이용해 후보를 걸러내면 훨씬 편리해진다.

profile
CSE

0개의 댓글