티스토리 뷰

지난 번에는 AES128을 활용한 이미지 암호화를 진행하였습니다.

이번에는 추가적으로 DES (single) 을 활용한 이미지 암호화를 진행해보겠습니다.

(라운드 별로 나오는 결과를 추후에 이용하고 싶어 라운드를 조작하는 부분을 추가하였습니다.)

 

우선 Single DES의 기본 구조를 살펴 보면 다음과 같습니다.

이처럼 평문 블럭은 64bit, 마스터키는 56bit, 라운드키는 48bit를 사용합니다. Initial Permutation과 16라운드, Final Permutation을 거쳐 최종 암호문 블럭을 얻게 됩니다.

 

 Initial Permutation과 Final Permutation은 64bit 평문을 섞어주는 역할을 합니다. 하지만 테이블이 주어져 있기 때문에 안전성에는 영향이 없습니다.

테이블은 다음과 같습니다. (Initial : 새로운 1번쨰 비트에 기존의 58번째 비트가 온다는 뜻, Final : 새로운 1번째 비트에 기존의 40번째 비트가 온다는 뜻, 가로방향, 오른쪽 순으로 진행)

라운드 함수는 다음과 같은 구조를 가지고 있습니다.

 

64비트 평문을 32비트씩 나누어 오른쪽 32비트를 키와 연산합니다. 그리고 이 둘을 XOR 후, Swap 한 것이 다음 라운드의 입력 블럭이 됩니다.

 

키와의 연산과정은 오른쪽 그림과 같은데 P-box와 S-box의 경우도 테이블이 주어져 있고, 키와 직접적인 연산은 XOR이 전부입니다.

 

각각의 테이블은 다음과 같습니다.

Expansion P-box

 

S-box

 

Straight P-box

이제 전체 과정을 살펴보면 다음과 같습니다. 마지막 라운드에는 swap 과정이 없음을 알 수 있고, 복호화 또한 암호화의 반대 과정을 그대로 따라 주면 됨을 알 수 있습니다.

 

다음으로 암호화에 필요한 키 생성 과정입니다.

1,2,9,16 라운드에서는 1비트 shift, 나머지는 2bit shift 입니다.

기존에 마스터키는 64bits 이지만, Parity Drop으로 56bit로 줄인뒤 Compression 과정을 통해 최종적으로 48bit의 키를 연산에 사용합니다.

 

이 또한 테이블이 주어져있고 다음과 같습니다.

Parity Drop
Compression P-box

 

이제 이미지 암호화를 진행해보겠습니다.

 

먼저 필요한 모듈을 import 하겠습니다. 저는 구글 코랩을 활용하여 구글 드라이브에 연결하는 것까지 포함했습니다.

import numpy as np
import copy
import random
from PIL import Image
from google.colab import drive
drive.mount('/gdrive', force_remount=True)
filename = '/gdrive/My Drive/Images/test_img/linux_penguin.jpeg'

이 녀석을 가져왔습니다.

DES에 필요한 변수값 테이블은 다음과 같습니다.

# Initial Permutation
IP = [58, 50, 42, 34, 26, 18, 10, 2,
      60, 52, 44, 36, 28, 20, 12, 4,
      62, 54, 46, 38, 30, 22, 14, 6,
      64, 56, 48, 40, 32, 24, 16, 8,
      57, 49, 41, 33, 25, 17, 9, 1,
      59, 51, 43, 35, 27, 19, 11, 3,
      61, 53, 45, 37, 29, 21, 13, 5,
      63, 55, 47, 39, 31, 23, 15, 7]

# Final Permutation
FP = [40, 8, 48, 16, 56, 24, 64, 32,
        39, 7, 47, 15, 55, 23, 63, 31,
        38, 6, 46, 14, 54, 22, 62, 30,
        37, 5, 45, 13, 53, 21, 61, 29,
        36, 4, 44, 12, 52, 20, 60, 28,
        35, 3, 43, 11, 51, 19, 59, 27,
        34, 2, 42, 10, 50, 18, 58, 26,
        33, 1, 41, 9, 49, 17, 57, 25]

# Expansion P-box
EP = [32, 1, 2, 3, 4, 5,
     4, 5, 6, 7, 8, 9,
     8, 9, 10, 11, 12, 13,
     12, 13, 14, 15, 16, 17,
     16, 17, 18, 19, 20, 21,
     20, 21, 22, 23, 24, 25,
     24, 25, 26, 27, 28, 29,
     28, 29, 30, 31, 32, 1]

#SBOX
S_Box = [
         
[[14, 4, 13, 1, 2, 15, 11, 8, 3, 10, 6, 12, 5, 9, 0, 7],
 [0, 15, 7, 4, 14, 2, 13, 1, 10, 6, 12, 11, 9, 5, 3, 8],
 [4, 1, 14, 8, 13, 6, 2, 11, 15, 12, 9, 7, 3, 10, 5, 0],
 [15, 12, 8, 2, 4, 9, 1, 7, 5, 11, 3, 14, 10, 0, 6, 13],
],

[[15, 1, 8, 14, 6, 11, 3, 4, 9, 7, 2, 13, 12, 0, 5, 10],
 [3, 13, 4, 7, 15, 2, 8, 14, 12, 0, 1, 10, 6, 9, 11, 5],
 [0, 14, 7, 11, 10, 4, 13, 1, 5, 8, 12, 6, 9, 3, 2, 15],
 [13, 8, 10, 1, 3, 15, 4, 2, 11, 6, 7, 12, 0, 5, 14, 9],
],

[[10, 0, 9, 14, 6, 3, 15, 5, 1, 13, 12, 7, 11, 4, 2, 8],
 [13, 7, 0, 9, 3, 4, 6, 10, 2, 8, 5, 14, 12, 11, 15, 1],
 [13, 6, 4, 9, 8, 15, 3, 0, 11, 1, 2, 12, 5, 10, 14, 7],
 [1, 10, 13, 0, 6, 9, 8, 7, 4, 15, 14, 3, 11, 5, 2, 12],
],

[[7, 13, 14, 3, 0, 6, 9, 10, 1, 2, 8, 5, 11, 12, 4, 15],
 [13, 8, 11, 5, 6, 15, 0, 3, 4, 7, 2, 12, 1, 10, 14, 9],
 [10, 6, 9, 0, 12, 11, 7, 13, 15, 1, 3, 14, 5, 2, 8, 4],
 [3, 15, 0, 6, 10, 1, 13, 8, 9, 4, 5, 11, 12, 7, 2, 14],
],  

[[2, 12, 4, 1, 7, 10, 11, 6, 8, 5, 3, 15, 13, 0, 14, 9],
 [14, 11, 2, 12, 4, 7, 13, 1, 5, 0, 15, 10, 3, 9, 8, 6],
 [4, 2, 1, 11, 10, 13, 7, 8, 15, 9, 12, 5, 6, 3, 0, 14],
 [11, 8, 12, 7, 1, 14, 2, 13, 6, 15, 0, 9, 10, 4, 5, 3],
], 

[[12, 1, 10, 15, 9, 2, 6, 8, 0, 13, 3, 4, 14, 7, 5, 11],
 [10, 15, 4, 2, 7, 12, 9, 5, 6, 1, 13, 14, 0, 11, 3, 8],
 [9, 14, 15, 5, 2, 8, 12, 3, 7, 0, 4, 10, 1, 13, 11, 6],
 [4, 3, 2, 12, 9, 5, 15, 10, 11, 14, 1, 7, 6, 0, 8, 13],
], 

[[4, 11, 2, 14, 15, 0, 8, 13, 3, 12, 9, 7, 5, 10, 6, 1],
 [13, 0, 11, 7, 4, 9, 1, 10, 14, 3, 5, 12, 2, 15, 8, 6],
 [1, 4, 11, 13, 12, 3, 7, 14, 10, 15, 6, 8, 0, 5, 9, 2],
 [6, 11, 13, 8, 1, 4, 10, 7, 9, 5, 0, 15, 14, 2, 3, 12],
],
   
[[13, 2, 8, 4, 6, 15, 11, 1, 10, 9, 3, 14, 5, 0, 12, 7],
 [1, 15, 13, 8, 10, 3, 7, 4, 12, 5, 6, 11, 0, 14, 9, 2],
 [7, 11, 4, 1, 9, 12, 14, 2, 0, 6, 10, 13, 15, 3, 5, 8],
 [2, 1, 14, 7, 4, 10, 8, 13, 15, 12, 9, 0, 3, 5, 6, 11],
]
]

# Straight P-box
SP = [16, 7, 20, 21, 29, 12, 28, 17,
     1, 15, 23, 26, 5, 18, 31, 10,
     2, 8, 24, 14, 32, 27, 3, 9,
     19, 13, 30, 6, 22, 11, 4, 25]

# Number of bit shifts
SHIFT = [1,1,2,2,2,2,2,2,1,2,2,2,2,2,2,1]

# Parity drop table
PD_Table = [57, 49, 41, 33, 25, 17, 9,
        1, 58, 50, 42, 34, 26, 18,
        10, 2, 59, 51, 43, 35, 27,
        19, 11, 3, 60, 52, 44, 36,
        63, 55, 47, 39, 31, 23, 15,
        7, 62, 54, 46, 38, 30, 22,
        14, 6, 61, 53, 45, 37, 29,
        21, 13, 5, 28, 20, 12, 4]

# Compression P-box
CP_Box = [14, 17, 11, 24, 1, 5, 3, 28,
        15, 6, 21, 10, 23, 19, 12, 4,
        26, 8, 16, 7, 27, 20, 13, 2,
        41, 52, 31, 37, 47, 55, 30, 40,
        51, 45, 33, 48, 44, 49, 39, 56,
        34, 53, 46, 42, 50, 36, 29, 32]

암호화에 필요한 함수 및 암호화 코드입니다.

(이미지 암호화를 다루기 위해 일반 평문 암호화와는 별개로 블럭 별 암호화 코드로 작성하였습니다.)

# 전체 키 구하기
def KeyScheduling(masterkey):
    # masterkey는 64bit=8bytes
    # 1byte 씩 8개 원소 갖는 list로 받기
    masterkey_list = []
    for num in masterkey:
        tmp = bin(num)
        tmp = tmp[2:]
        tmp = list(tmp)
        if len(tmp) < 8:
            for _ in range(8-len(tmp)):
                tmp.insert(0,'0')
        
        for bit in tmp:
            masterkey_list.append(bit)
    
    CipherKey = []
    for idx in PD_Table:
        CipherKey.append(masterkey_list[idx-1])
    
    WholeKey = []
    Left = copy.deepcopy(CipherKey[:28])
    Right = copy.deepcopy(CipherKey[28:])
    
    for rnd in range(16):
        Left = Left[SHIFT[rnd]:]+Left[:SHIFT[rnd]]
        Right = Right[SHIFT[rnd]:]+Right[:SHIFT[rnd]]
        
        # R_key : 라운드별 키 (48bits)
        R_key = ''
        tmp = Left+Right
        for idx in CP_Box:
            R_key += tmp[idx-1]
        
        WholeKey.append(R_key)
    
    # 각 라운드 별 key 리턴 (48bit, list of string)
    return WholeKey

# Initial Permutation
def InitialPermutation(plaintext):
    # plaintext 64bit=8bytes=1block
    # 1byte 씩 8개 원소 갖는 list로 받기
    plain = ''
    for num in plaintext:
        tmp = bin(num)
        tmp = tmp[2:]
        tmp = list(tmp)
        if len(tmp) < 8:
            for _ in range(8-len(tmp)):
                tmp.insert(0,'0')
        
        for bit in tmp:
            plain += bit
    
    init_per = ''
    for idx in IP:
        init_per += plain[idx-1]
    
    # init_per : Initial Permutation 결과
    # 64bit, string
    return init_per

# Final Permutation
def FinalPermutation(ciphertext):
    # plaintext 64bit=8bytes=1block
    # 1byte 씩 8개 원소 갖는 list로 받기
    cipher = ''
    for num in ciphertext:
        tmp = bin(num)
        tmp = tmp[2:]
        tmp = list(tmp)
        if len(tmp) < 8:
            for _ in range(8-len(tmp)):
                tmp.insert(0,'0')
        
        for bit in tmp:
            cipher += bit
    
    final_per = ''
    for idx in FP:
        final_per += cipher[idx-1]
    
    # init_per : Initial Permutation 결과
    # 64bit, string
    return final_per

def RoundFunction(text, key):
    # text : 64bit string
    # key : 48bit string
    # Swapping 따로 (마지막 라운드는 swap X)
    Left = text[:32]
    Right = text[32:]
    
    expansion = ''
    for idx in EP:
        expansion += Right[idx-1]
    
    xor = ''
    for i in range(len(key)):
        bit1 = int(expansion[i])
        bit2 = int(key[i])
        bit = bit1 ^ bit2
        xor += str(bit)
    
    S_input = []
    for i in range(0, len(xor), 6):
        tmp = copy.deepcopy(xor[i:i+6])
        S_input.append(tmp)
    
    S_output = ''
    for idx in range(8):
        row = ''
        column = ''
        row += S_input[idx][0]
        row += S_input[idx][5]
        column += S_input[idx][1:5]
        row = int(row, 2)
        column = int(column, 2)
        
        entry = S_Box[idx][row][column]
        entry = bin(entry)
        entry = entry[2:]
        
        if len(entry) < 4:
            tmp = ''
            for i in range(4-len(entry)):
                tmp += '0'
            
            entry = tmp+entry
        
        S_output += entry
    
    f_output = ''
    for idx in SP:
        f_output += S_output[idx-1]
    
    mixer = ''
    for i in range(len(f_output)):
        bit1 = int(Left[i])
        bit2 = int(f_output[i])
        bit = bit1 ^ bit2
        mixer += str(bit)
    
    # mixer, Right : 32bit, string
    return mixer, Right

def Swapper(left, right):
    return right, left

def DES_encryption(plainblock, masterkey, round=16):
    # plainblock : 64bit
    # 1byte씩 8개 원소 list로 받기
    # masterkey : 64bit
    # 1byte씩 8개 원소 list로 받기
    wholekey = KeyScheduling(masterkey)
    block = copy.deepcopy(plainblock)
    block = InitialPermutation(block)
    
    left = ''
    right = ''
    for i in range(round-1):
        left, right = RoundFunction(block, wholekey[i])
        left, right = Swapper(left, right)
        block = left+right
    
    left, right = RoundFunction(block, wholekey[round-1])
    
    block = left+right
    cipherblock = []
    for i in range(0,len(block),8):
        tmp = block[i:i+8]
        cipherblock.append(int(tmp,2))
    
    final_per = FinalPermutation(cipherblock)
    
    cipherblock = []
    for i in range(0,len(final_per),8):
        cipherblock.append(int(final_per[i:i+8], 2))
    
    # cipherblock : 1byte씩 8개 원소 list
    return cipherblock

 다음으로 ECB, CBC, OFB 모드에 대한 함수입니다.

def DES_ECB(plain, masterkey, round=16):
  # plain : image to array, shape = (128,,128,3) 으로 받기
  # key : 64bits=8bytes, 1bytes 8개 list로 받기
  plain = plain.transpose(2,0,1)
  plain_list = plain.tolist()

  cipher_ecb = []

  ylen = len(plain_list[0])
  xlen = len(plain_list[0][0])
  channel = len(plain_list)
  for i in range(channel):
    cipher = []
    for j in range(ylen):
      cipherblocks = []
      for k in range(0,xlen,8):
        plainblock = copy.deepcopy(plain_list[i][j][k:k+8])
        cipherblocks += DES_encryption(plainblock, masterkey, round)
      cipher.append(copy.deepcopy(cipherblocks))
    cipher_ecb.append(copy.deepcopy(cipher))
  
  cipher_ecb = np.array(cipher_ecb)
  cipher_ecb = cipher_ecb.transpose(1,2,0)
  return cipher_ecb
def DES_CBC(plain, masterkey, IV = [0,0,0,0,0,0,0,0], round=16):
  # plain : image to array, shape = (128,,128,3) 으로 받기
  # key : 64bits=8bytes, 1bytes 8개 list로 받기
  plain = plain.transpose(2,0,1)
  plain_list = plain.tolist()
  iv = IV

  cipher_cbc = []

  ylen = len(plain_list[0])
  xlen = len(plain_list[0][0])
  channel = len(plain_list)
  for i in range(channel):
    cipher = []
    for j in range(ylen):
      cipherblocks = []
      for k in range(0,xlen,8):
        plainblock = copy.deepcopy(plain_list[i][j][k:k+8])
        for s in range(len(plainblock)):
          plainblock[s] ^= iv[s]
        cipherblock = DES_encryption(plainblock, masterkey, round)
        iv = copy.deepcopy(cipherblock)
        cipherblocks += cipherblock
      cipher.append(copy.deepcopy(cipherblocks))
    cipher_cbc.append(copy.deepcopy(cipher))
  
  cipher_cbc = np.array(cipher_cbc)
  cipher_cbc = cipher_cbc.transpose(1,2,0)
  return cipher_cbc
def DES_OFB(plain, masterkey, IV = [0,0,0,0,0,0,0,0], round=16):
  # plain : image to array, shape = (128,,128,3) 으로 받기
  # key : 64bits=8bytes, 1bytes 8개 list로 받기
  plain = plain.transpose(2,0,1)
  plain_list = plain.tolist()
  iv = IV

  cipher_ofb = []

  ylen = len(plain_list[0])
  xlen = len(plain_list[0][0])
  channel = len(plain_list)
  for i in range(channel):
    cipher = []
    for j in range(ylen):
      cipherblocks = []
      for k in range(0,xlen,8):
        plainblock = copy.deepcopy(plain_list[i][j][k:k+8])
        iv = DES_encryption(iv, masterkey, round)
        input_vec = copy.deepcopy(iv)
        for s in range(len(iv)):
          iv[s] ^= plainblock[s]
        cipherblocks += iv
        iv = input_vec
      cipher.append(copy.deepcopy(cipherblocks))
    cipher_ofb.append(copy.deepcopy(cipher))
  
  cipher_ofb = np.array(cipher_ofb)
  cipher_ofb = cipher_ofb.transpose(1,2,0)
  return cipher_ofb

키를 [1,2,3,4,5,6,7,8] 로 잡은 뒤 각각의 모드에 대해 암호화를 진행해보도록 하겠습니다.

test_arr = DES_ECB(arr, [1,2,3,4,5,6,7,8], round=16)
test_arr2 = DES_CBC(arr, [1,2,3,4,5,6,7,8], round=16)
test_arr3 = DES_OFB(arr, [1,2,3,4,5,6,7,8], round=16)

matplotlib를 활용해 다음과 같음을 알 수 있습니다.

ECB
CBC
OFB

 

추가 : AES의 경우, CBC와 OFB가 라운드 별로 암호하에 대한 결과가 육안으로 보기에는 크게 다르지 않았는데 DES의 경우 실제 실험 시 1,2라운드 정도의 적은 라운드 암호화 진행시 암호화가 잘 이루어지지 않음을 알 수 있습니다.

이 부분에 대해서는 다음 번에 포스팅하도록 하겠습니다.

댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
TAG
more
«   2025/02   »
1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28
글 보관함