학사 나부랭이
Cryptography - Opening, 고전 암호 본문
암호
- 키가 있어야 정상적으로 해독 가능
- 보안을 위해 변환
- 평문-(암호화)->암호문-(복호화)->평문
- 단, 해시값은 복호화 불가
코드
- 변환 규칙을 공개해서 누구나 해독 가능
- 편의를 위해 변환
- 평문-(인코딩)->코드-(디코딩)->평문
들어가기 전에 가벼운 파이썬
리스트
- 배열인데 자료형을 따지지 않음 (ex. li = [1, 2.4, 'a', 'asdf', ['가', 0, 3.1]])
- 리스트 객체의 append()로 원소 추가 (ex. li.append(4) == [1, 2.4, 'a', 'asdf', ['가', 0, 3.1], 4])
- 리스트 객체의 remove()나 del 키워드로 원소 삭제 (ex. li.remove('asdf') or del li[3] == [1, 2.4, 'a', ['가', 0, 3.1]])
딕셔너리
- 키:값으로 쌍을 이루는 순서가 정해지지 않은 배열 (ex. dc = {'pura':'sino', 'pru':'ssia'})
- 딕셔너리 객체[키] = 값으로 원소 추가 (ex. dc['pra'] = 'china' == {'pura':'sino', 'pru':'ssia', 'pra':'china'}
- 딕셔너리 객체의 keys()로 키 추출 (ex. dc.keys() == dict_keys(['pura', 'pru', 'pra'])
- 딕셔너리 객체의 values()로 값 추출 (ex. dc.values() == dict_values(['sino', 'ssia', 'china'])
- del 키워드로 원소 삭제 (ex. del dc['pru'] == {'pura':'sino', 'pra':'china'}
lambda 함수
- lambda 변수, 변수 , ... : 식, 인자, 인자, ... 의 형태
- 한 번 쓰고 버리는 일회성 함수
- 인자들을 이용한 "식"의 결과를 리턴 (ex. lambda x, y: x * y, 2, 3 == 6)
map 함수
- 함수와 반복 가능한 자료형(이하 배열)을 인자로 받음
- 배열의 원소를 함수에 넣어 리턴 값으로 다시 배열을 만듦 (ex. map(sqare, [0, 1, 2] == [0, 1, 4])
간단한 암호화 코드
def makeCodebook():
decbook = {'5':'a', '2':'b', '#':'d', '8':'e',
'1':'f', '3':'g', '4':'h', '6':'i',
'0':'l', '9':'m', '*':'n', '%':'o',
'=':'p', '(':'r', ')':'s', ';':'t',
'?':'u', '@':'v', ':':'y', '7':' '}
encbook = {}
for k in decbook:
val = decbook[k]
encbook[val] = k #decbook의 키와 값을 반전
return encbook, decbook
def encrypt(msg, encbook):
for c in msg:
if c in encbook:
msg = msg.replace(c, encbook[c]) #msg의 문자 하나를 대체
return msg
def decrypt(msg, decbook):
for c in msg:
if c in decbook:
msg = msg.replace(c, decbook[c])
return msg
if __name__ == '__main__':
plaintext = 'ⅰlove you with all my heart'
encbook, decbook = makeCodebook()
print(encbook)
print(decbook)
ciphertext = encrypt(plaintext, encbook)
print(ciphertext)
deciphertext = decrypt(ciphertext, decbook)
print(deciphertext)
글자를 교환한 환자(換字)암호
ENC = 0
DEC = 1
def makeDisk(key):
keytable = map(lambda x: (chr(x + 65), x), range(26)) #lambda 결과: ([chr(x + 65):x])map 결과: [A:0, B:1, ..., Z:25]
key2index = {}
for t in keytable:
alphabet, index = t[0], t[1]
key2index[alphabet] = index #keytable 키와 값을 반전
print(key2index)
if key in key2index:
k = key2index[key]
else:
return None, None
enc_disk = {}
dec_disk = {}
for i in range(26):
enc_i = (i + k) % 26
enc_ascii = enc_i + 65 #key 만큼 알파벳 순서의 뒤로 미룸
enc_disk[chr(i+65)] = chr(enc_ascii) #a = a + key 의 딕셔너리 딕셔너리
dec_disk[chr(enc_ascii)] = chr(i + 65) #a + key = a 의 딕셔너리
return enc_disk, dec_disk
def caesar(msg, key, mode):
ret = ''
msg = msg.upper()
enc_disk, dec_disk = makeDisk(key)
if enc_disk is None:
return ret
if mode is ENC:
disk = enc_disk
if mode is DEC:
disk = dec_disk
for c in msg:
if c in disk:
ret += disk[c]
else:
ret += c
return ret
def main():
plaintext = 'BECARFULFORASSASSINATE'
key = 'F'
print('origin:\t\t%s', plaintext.upper())
ciphertext = caesar(plaintext, key, ENC)
print('cipherted:\t\t%s', ciphertext)
deciphertext = caesar(ciphertext, key, DEC)
print('deciphered:\t\t%s', deciphertext)
if __name__ == '__main__':
main()
문자의 자리를 바꾸는 전치(転置) 암호 ~Ceasar cipher~
모든 전치 암호는 환자 암호이며 전치 암호가 아닌 것 같지만 전치 암호인 것도 있다. (ex. 한 비트씩 미루기)
위의 카이사르(시저) 암호의 암호키는 많아봤자 26개이다. 그래서 아래처럼 이를 변형해 암호키의 개수를 늘린 Affine cipher도 있다.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~(전략)~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
for i in range(26):
enc_i = (k1 * i + k2) % 26
enc_ascii = enc_i + 65
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~(후략)~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
k1은 값의 중복을 방지하기 위해 26과 서로소(공약수가 없음)이며 k2는 25 이하의 값이다.
주상(柱状) 전치 암호
평문을 배열한 후 열 방향으로 문장을 구성한다.
이 경우 암호문은 CGKOSBFJNRDHLPTAEIMQ이고 암호키는 4213이다.
ENC = 0
DEC = 1
def parseKey(key):
tmp = []
key = key.upper()
for i, k in enumerate(key): #index 부여 == [(0, B), (1, R), ...]
tmp.append((i, k)) #(0, B)부터 삽입
tmp = sorted(tmp, key=lambda x:x[1]) #알파벳 순으로 정렬 == [(2, A), (0, B), ...]
print('tmp: ', tmp)
enc_table = {}
dec_table = {}
for i, r in enumerate(tmp): #i = enumerate로 붙은 index, r = (2, A)
enc_table[r[0]] = i #enct[2] = 0, ...
dec_table[i] = r[0] #dect[0] = 2, ...
print('enc-table: ', enc_table)
print('dec-table: ', dec_table)
return enc_table, dec_table
def transposition(msg, key, mode):
msgsize = len(msg)
keysize = len(key)
ret = ''
filler = ''
if msgsize%keysize != 0:
filler = '0'*(keysize - msgsize%keysize) #패딩(마지막 블록을 다 채워서 하나의 블록으로 만들기 위해)
msg = msg.upper()
msg += filler
enc_table, dec_table = parseKey(key)
if mode == ENC:
table = enc_table
else:
table = dec_table
if mode == ENC:
buf = ['']*keysize #짤의 열 개수
for i, c in enumerate(msg):
col = i%keysize #이차원 배열의 행
index = table[col] #행이 섞인대로 넣어야제!
buf[index] += c #하모 그렇제!
for text in buf:
ret += text #그걸 한 줄로 보내야제!
else:
blocksize = int(msgsize/keysize) # m개의 문자를 k(5) 개의 버퍼로 출력할 때 필요한 크기
buf = ['']*keysize
pos = 0
for i in range(keysize):
text = msg[pos:pos+blocksize] #block 크기만큼 잘라서
print(text)
index = table[i]
buf[index] += text
pos += blocksize #네 다음 블록~
for i in range(blocksize):
for j in range(keysize):
if buf[j][i] != '0': #패딩한거 빼고
ret += buf[j][i]
return ret
def main():
key = 'BRAIN'
msg = 'TREASUREBOXISBURRIEDATTWOHUNDREDFEETTONORTHEASTAWAYFROMYOURHOME'
ciphertext = transposition(msg, key, ENC)
deciphertext = transposition(ciphertext, key, DEC)
print('Original:\t\t%s' %msg.upper())
print('Ciphered:\t\t%s' %ciphertext)
print('Deciphered:\t\t%s' %deciphertext)
if __name__ == '__main__':
main()
Comments