[ AES 암호의 특징과 구조 ]

cryptosecurity.tistory.com/29

 

AES 암호의 특징과 구조

AES의 특징 1. 128비트 블록단위로 암호화하는 대칭 암호 알고리즘 2. 키의 비트 길이는 128, 192, 256비트 3. 10/12/14 라운드 4. SPN 구조 5. 바이트 단위의 연산 위주 AES의 구조 1. SubBytes 2. ShiftRows 3...

cryptosecurity.tistory.com

 

 

AES Cipher 암호화, 복호화 파이썬으로 구현하기

1. Sbox, ISBox, RC 설정

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
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
# Rijndael S-box
Sbox =  [ 0x630x7c0x770x7b0xf20x6b0x6f0xc50x300x010x67,
        0x2b0xfe0xd70xab0x760xca0x820xc90x7d0xfa0x59,
        0x470xf00xad0xd40xa20xaf0x9c0xa40x720xc00xb7,
        0xfd0x930x260x360x3f0xf70xcc0x340xa50xe50xf1,
        0x710xd80x310x150x040xc70x230xc30x180x960x05,
        0x9a0x070x120x800xe20xeb0x270xb20x750x090x83,
        0x2c0x1a0x1b0x6e0x5a0xa00x520x3b0xd60xb30x29,
        0xe30x2f0x840x530xd10x000xed0x200xfc0xb10x5b,
        0x6a0xcb0xbe0x390x4a0x4c0x580xcf0xd00xef0xaa,
        0xfb0x430x4d0x330x850x450xf90x020x7f0x500x3c,
        0x9f0xa80x510xa30x400x8f0x920x9d0x380xf50xbc,
        0xb60xda0x210x100xff0xf30xd20xcd0x0c0x130xec,
        0x5f0x970x440x170xc40xa70x7e0x3d0x640x5d0x19,
        0x730x600x810x4f0xdc0x220x2a0x900x880x460xee,
        0xb80x140xde0x5e0x0b0xdb0xe00x320x3a0x0a0x49,
        0x060x240x5c0xc20xd30xac0x620x910x950xe40x79,
        0xe70xc80x370x6d0x8d0xd50x4e0xa90x6c0x560xf4,
        0xea0x650x7a0xae0x080xba0x780x250x2e0x1c0xa6,
        0xb40xc60xe80xdd0x740x1f0x4b0xbd0x8b0x8a0x70,
        0x3e0xb50x660x480x030xf60x0e0x610x350x570xb9,
        0x860xc10x1d0x9e0xe10xf80x980x110x690xd90x8e,
        0x940x9b0x1e0x870xe90xce0x550x280xdf0x8c0xa1,
        0x890x0d0xbf0xe60x420x680x410x990x2d0x0f0xb0,
        0x540xbb0x16]
 
# Rijndael Inverted S-box
ISbox = [ 0x520x090x6a0xd50x300x360xa50x380xbf0x400xa3,
        0x9e0x810xf30xd70xfb , 0x7c0xe30x390x820x9b0x2f,
        0xff0x870x340x8e0x430x440xc40xde0xe90xcb , 0x54,
        0x7b0x940x320xa60xc20x230x3d0xee0x4c0x950x0b,
        0x420xfa0xc30x4e , 0x080x2e0xa10x660x280xd90x24,
        0xb20x760x5b0xa20x490x6d0x8b0xd10x25 , 0x720xf8,
        0xf60x640x860x680x980x160xd40xa40x5c0xcc0x5d,
        0x650xb60x92 , 0x6c0x700x480x500xfd0xed0xb90xda,
        0x5e0x150x460x570xa70x8d0x9d0x84 , 0x900xd80xab,
        0x000x8c0xbc0xd30x0a0xf70xe40x580x050xb80xb3,
        0x450x06 , 0xd00x2c0x1e0x8f0xca0x3f0x0f0x020xc1,
        0xaf0xbd0x030x010x130x8a0x6b , 0x3a0x910x110x41,
        0x4f0x670xdc0xea0x970xf20xcf0xce0xf00xb40xe6,
        0x73 , 0x960xac0x740x220xe70xad0x350x850xe20xf9,
        0x370xe80x1c0x750xdf0x6e , 0x470xf10x1a0x710x1d,
        0x290xc50x890x6f0xb70x620x0e0xaa0x180xbe0x1b ,
        0xfc0x560x3e0x4b0xc60xd20x790x200x9a0xdb0xc0,
        0xfe0x780xcd0x5a0xf4 , 0x1f0xdd0xa80x330x880x07,
        0xc70x310xb10x120x100x590x270x800xec0x5f , 0x60,
        0x510x7f0xa90x190xb50x4a0x0d0x2d0xe50x7a0x9f,
        0x930xc90x9c0xef , 0xa00xe00x3b0x4d0xae0x2a0xf5,
        0xb00xc80xeb0xbb0x3c0x830x530x990x61 , 0x170x2b,
        0x040x7e0xba0x770xd60x260xe10x690x140x630x55,
        0x210x0c0x7d]
 
# Round constant for Key schedule
RC = [ 0x010x020x040x080x100x200x400x800x1B0x36]
cs

 

 

2. 16byte를 4x4 행렬(state)로 변환하고 출력해주는 함수 구현하기

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#-- 16바이트를 4x4 행렬(state)로 변환 
def block2state(in_block):
    new_state = []
    for col in range(4):
        new_col = [ in_block[col*4+i] for i in range(4) ]
        new_state.append(new_col)
    return new_state
 
#-- (4x4 state) 출력
def hex_print(state):
    print('[', end='')
    for i in range(4):
        print('[%02x, %02x, %02x, %02x]' \
              %(state[i][0], state[i][1], state[i][2], state[i][3]), end='')
        if i<3:
            print(', ', end='')
    print(']')
cs

 

 

 

3. 유한체 GF(2^8) 상에서의 곱셈 함수 구현하기

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#-- xtime 유한체 GF(2^8)에서의 곱셈 (다항식에 x를 곱하기)
def xtime(x):
    y = (x << 1& 0xff
    if x >= 128:
        y ^= 0x1b
    return y
 
#-- MixColumn 용 '0x02' 곱하기
def m02(x):
    return xtime(x)
 
#-- MixColumn 용 '0x03' 곱하기
def m03(x):
    return m02(x)^x
cs

 

 

 

4. 한 Column에 대한 MixColumns 연산, SBox 연산, Xor연산 함수 구현하기

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#-- 한 Column에 대한 MixColumns 연산
    # [ [2, 3, 1, 1], [1, 2, 3, 1], [1, 1, 2, 3], [3, 1, 1, 2] ]
def MC_Col(col):
    new_col = [0]*4
    new_col[0= m02(col[0]) ^ m03(col[1]) ^ col[2] ^ col[3]
    new_col[1= col[0] ^ m02(col[1]) ^ m03(col[2]) ^ col[3]
    new_col[2= col[0] ^ col[1] ^ m02(col[2]) ^ m03(col[3])
    new_col[3= m03(col[0]) ^ col[1] ^ col[2] ^ m02(col[3])
    return new_col
 
#-- 한 column에 대한 Sbox
def SubByte_Col(col):
    new_col = [ Sbox[col[i]] for i in range(4) ]
    return new_col
 
#-- 한 Column의 XOR
def Xor_Col(c1, c2):
    new_col = [ c1[i]^c2[i] for i in range(4) ]
    return new_col
cs

 

 

 

5. SubBytes, ShiftRows, MixColums, AddRoundKey 함수 구현하기

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
29
30
31
#-- SubBytes
def SubBytes(state):
    new_state = []
    for col in range(4):
        new_col = [ Sbox[ state[col][i] ] for i in range(4) ]
        new_state.append(new_col)
    return new_state
 
#-- ShiftRows
def ShiftRows(state):
    new_state = []
    for col in range(4):
        new_col = [ state[(col+i)%4][i] for i in range(4) ]
        new_state.append(new_col)
    return new_state
 
#-- MixColumns
def MixColumns(state):
    new_state = []
    for col in range(4):
        new_col = MC_Col(state[col])
        new_state.append(new_col)
    return new_state
 
#-- AddRoundkey
def AddRoundKey(state, rkey):
    new_state = []
    for col in range(4):
        new_col = [ state[col][i] ^ rkey[col][i] for i in range(4) ]
        new_state.append(new_col)
    return new_state
cs

 

 

 

6. 라운드 함수 구현하기

1
2
3
4
5
6
7
8
#-- 라운드 함수
def AES_Round(state, rkey):
    new_state = copy.deepcopy(state)
    new_state2 = SubBytes(new_state)
    new_state3 = ShiftRows(new_state2)
    new_state4 = MixColumns(new_state3)
    new_state5 = AddRoundKey(new_state4, rkey)
    return new_state5
cs

 

 

 

7. key schedule 함수 구현하기

[ AES key schedule에서만 사용되는 4바이트 변환함수 ]

    -  바이트 rotation -> Sbox 적용 -> RoundConstant 적용

1
2
3
4
5
6
def KeySR(col, round):
    new_col = Rotl(col)
    round_constant = [ RC[round-1], 000]
    new_col2 = SubByte_Col(new_col)
    new_col3 = Xor_Col(new_col2, round_constant)
    return new_col3
cs

 

[ AES Encrytion용 Key schedule ]

   - 입력: 암호키 (4x4 state)

   - 출력: 11개의 라운드키(4x4 state)

   - 출력 rkey = [ rkey[0], rkey[1], ... , rkey[10] ]

   - rkey[r] = [ [rk00, rk10, rk20, rk30], ... , [rk03, rk13, rk23, rk33] ]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
def key_schedule_Enc(key_state):
    rkey = [ copy.deepcopy(key_state) ]
    for round in range(1,11):
        new_state = []
        new_w0 = Xor_Col(rkey[round-1][0], KeySR(rkey[round-1][3], round))
        new_state.append(new_w0)
        new_w1 = Xor_Col(rkey[round-1][1], new_w0)
        new_state.append(new_w1)
        new_w2 = Xor_Col(rkey[round-1][2], new_w1)
        new_state.append(new_w2)
        new_w3 = Xor_Col(rkey[round-1][3], new_w2)
        new_state.append(new_w3)
        rkey.append(new_state)
    return rkey
cs

 

 

 

8. AES 128 암호화 구현하기

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#-- AES-128 암호화
def AES_Enc(pt, key):
    rkey = key_schedule_Enc(key)
    state = copy.deepcopy(pt)
    new_state = AddRoundKey(state, rkey[0])
    for i in range(1,10):  # 1,2,...,9
        out_state = AES_Round(new_state, rkey[i])
        new_state = copy.deepcopy(out_state)
        print(i, ': ', end ='')
        hex_print(new_state)
    #-- final round
    new_state2 = SubBytes(new_state)
    new_state3 = ShiftRows(new_state2)
    new_state4 = AddRoundKey(new_state3, rkey[10])
    return new_state4
cs

final round (10라운드) 부분은 MixColums이 없기 때문에 따로 써준다.

 

 

9. 암호화 테스트

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
def main():
    # 테스트 벡터: FIPS 197 - AES (page 33)
    block = [ 0x320x430xf60xa80x880x5a0x300x8d, \
            0x310x310x980xa20xe00x370x070x34 ]
    key = [ 0x2b0x7e0x150x160x280xae0xd20xa6, \
            0xab0xf70x150x880x090xcf0x4f0x3c ]
    
    in_state = block2state(block)
    key_state = block2state(key)
    
    print('plaintext =')
    hex_print(in_state)
    print('key=')
    hex_print(key_state)
    
    new_state = AES_Enc(in_state, key_state)
    print('ciphertext=')
    hex_print(new_state)
    
if __name__ == '__main__':
    main()        
cs

 

== 실행결과 ==

 

'Cipher analysis > AES' 카테고리의 다른 글

AES 암호의 특징과 구조  (0) 2020.10.12
[Python] AES, Hash 구현 - Crypto 모듈 이용  (0) 2020.09.12

AES의 특징

1. 128비트 블록단위로 암호화하는 대칭 암호 알고리즘

2. 키의 비트 길이는 128, 192, 256비트

3. 10/12/14 라운드

4. SPN 구조

5. 바이트 단위의 연산 위주

 

 

AES의 구조

 

 

 

 

 

 

1. SubBytes

2. ShiftRows

3. MixColumns

4. AddRoundKey

 

 

 

 

 

 

 

 

- 마지막 라운드에는 MixColumns 수행하지 않음

- 구현 효율성과 암호화 / 복호화 대칭성에 유리하기 때문

 

SubBytes

Rijndaek field : GF(2^8) = GF(2)[X] / <m(x)>

M(x) = x^8+x^4+x^3+x+1

SubBytes 함수 : y = S(x) = Ax^(-1) + b

-> GF(2^8)에서 mod m(x) 상에서의 inverse계산

- >Affine 변환 적용(GF(2) 상)

 

AES의 S-Box

 

ShiftRows

각 행을 위로부터 0,1,2,3 만큼 왼쪽으로 회전

MixColumns 과 함께 확산효과를 위하여 사용

 

MixColumns

- 열 단위 확산함수

- 각 열을 GF(2^8) 상의 벡터로 간주한 선형 변환

- 각 열을 c(x) = 03x^3 + 01x^2 + 01x + 02와 mod x^4 + 1 상에서 곱한다.

- b(x) = c(x) * a(x) mod x^4 + 1

 

 

AES 라운드 함수의 테이블 구조 구현 1

- SubBytes, ShiftRows, MixColums

 

AES 라운드 함수의 테이블 구조 구현 2

- AddRoundKey

 

1. JEB 실행하기

1) JEB, APK 설치

 

2) JAVA 설치

 

3) JEB 실행

 

4) 파일 - 열기 - 앱 선택

 

5) Project Explore에서 앱 더블클릭 -> bytecode 더블클릭

 

6) classese.dex 파일 보여줌

 

 

2. JEB로 apk 분석

 

4) Q 눌러서 JAVA 코드로 변환

 

 

3. 의미있는 함수 찾기

3.1) AuthToken 구하기

  • decryptCipher()

getAuthToken() 반환

 

 

  • getAuthToken()

getAuthToken  =  [ RestKey : RestSecret ]

 

 

  • getString()

ttkey01의 String 얻는 함수

RestKey  =  ttkey01의 String

 

 

  • getSecureString()

RestSecret =  decrypt( ttkey02의 String )

 

 

  • decrypt()

v0 = RestKey

 

 

  • decrypt().a()

RestKey를 평문으로 받아 Secretkey 구하는 함수

 

 

  • decrypt().a().c()

 

Secretkey = PBKDF2-SHA1 ( restkey, salt, iteration=1000, keylen = 256 )

 

  • decrypt().a()

 

 

  • decrypt().a().a()

AES, ECB, PKCS5패딩, KEY=secretkey로 복호화를 정의하는 함수

 

 

  • decrypt()

decrypt(ttkey02)

1. ttkey02로 restkey 생성

2. restkey로 pbkdf-sha1 이용해서 secretkey 생성

3. 복호화  ->  AES, ECB, PKCS5, secretkey

4.  UTF-8로 디코딩해서 출력

 

 

 

 

  • getAuthToken()

Authtoken = [RestKey : RestSecret] = [ ttkey01의 String  : decrypt ( ttkey02의 String) ]

 

 

 

3.2 ) AuthToken을 이용해서 Secretkey 2 얻기

  • decryptCipher()

 

 

  • decryptCipher().c()

 

Secretkey2 = PBKDF2-SHA1 ( AuthToken, salt, iteration = 1000, keylen = 256 )

 

 

 

3.3) decryptCipher 구하기

  • decryptCipher()

 

 

  • decryptCipher().a()

 

 

  • decryptCipher()

 

 

4. tigerconnect.apk의 decrypt 구조화하기

[ FBE(File-Based Encryption)의 동작 과정 ]

  • 파일 시스템 수준에서 수행
  • ext4 파일 시스템 암호화

 

1. FDE와 유사하게 User key와 salt를 이용해서 KDF를 이용해 KEK, IV를 생성

 

 

 

 

 

 

 

 KEK는 TEE에 안전하게 보관된다.

 

 

 

 

 


2. KEK와 IV를 사용하여 AES-256-GCM으로 DEK를 암호화

  

     - DEK : dev/urandom에서 읽은 64byte 난수

     - 상위 32byte는 파일 내용 암호화하는데 사용

     - 하위 32byte는 파일 이름 암호화하는데 사용

 

 

      TEE에서 KEK를 가져오려면 3가지 요구사항을 충족해야 한다.

      (1) 확장된 사용자 자격증명

               :  KDF(scrypt)로 사용자 인증을 확장

      (2) Secdiscardable hash (사용자 SID)

               :  임의의 16KB 파일로 이루어진 512bit 해시

      (3) 인증 토큰

               :  사용자가 정상적으로 로그인했을 때 게이트키퍼에 의해

                  생성되는 암호화방식으로 인증된 토큰


3. DEK와 nonce를 사용하여 AES-ECB-128로 암호화해서 각 파일별 키 생성 

 

 

 

  • 각 파일에는 연결된 16byte nonce가 dentry에 저장
  • dentry는 ext4 파일 시스템 구조에 inode항목에 연결하는 데이터 구조

 

  • DEK 상위 32byte -> AES-ECB-128 -> 파일 이름 암호화 키 생성
  • DEK 하위 32byte -> AES-ECB-128 -> 파일 내용 암호화 키 생성


4.  파일 이름은 각 파일별 키와 IV를 사용하여                                                                                                          AES-256-CBC-CTS로 암호화 된다.

  • IV는 0으로 채워진다.
  • 결과 암호문은 base62 인코딩을 사용하여 인코딩 된다.
  • 암호화된 파일 이름은 dentry에 저장된다.

 

5. 파일 내용은 각 파일별 키를 사용하여                                                                                                                    AES-256-XTS로 암호화된다.

  • 암호화된 파일 내용은 inode에 저장된다.

6.  Key Store

‘crypto footer’ 유추  ->  /data/misc/vold/user_keys 디렉터리

‘de’와 ‘ce’ 두 개의 하위 디렉터리로 나뉜다.

 

=================================================

 

“encrypted_key”   :  암호화된 DEK 포함

“keymaster_key_blob   :  암호화된 RSA-2048 개인키 포함

“salt”   :  DEK를 복호화할 때 사용자 인증과 결합하여 사용

                   -> de 디렉터리는 사용자 입력이 필요하지 않으므로 salt 없음

“secdiscardable   :  임의의 16KB 파일의 512bit 해시 값

“stretching”   :   KDF에 사용되는 매개변수 포함

                   -> “scrypt X : Y : Z” 형식

                   ->  N = 2^X,  r = 2^Y,  p = 2^Z

1. FBE(File-Based Encryption)란?

  • 여러 키를 사용하여 여러 파일을 암호화 가능
  • 안드로이드 7.0부터 FBE 추가
  • 직접 부팅(Direct Boot) 기능 추가
    • 인증하지 않은 상태에서 바로 부팅 가능
    • 그러나 중요한 데이터는 복호화되지 않음
  • DE와 CE로 나눠서 각각 암호화하고 각각의 접근 범위를 설정한다.
    • CE(Credential Encrypted) storage  :  기본 저장공간, 사용자 인증 이후로만 사용 가능한 저장소
    • DE(Device Encrypted) storage  :  사용자 인증과 상관없이 사용 가능한 저장소(직접 부팅, 인증 이후 모두 가능)
  • 접근성이 설정된다면?
    • DE영역만 접근하도록 하여 데이터 보호 -> 암호화 무력화 문제 해결

 

2. FBE Decryption

• 파일 수준에서 암,복호화

• 기본 ext4 파일 시스템이 지원하는 fscrypt 암호화를 사용.

• 별도의 암호화된 두 가지 스토리지 영역

    - DE : 사용자 인증 전에 사용 가능.

    - CE : 사용자 인증 후에만 사용 가능.

1. 먼저 KEK를 생성하는데, FDE와 비슷한 방식으로 생성.

2. AES-256-GCM을 이용하여 CE, DE 별도의 마스터키가 생성.

3. AES-128-ECB와 128bit nonce를 이용하여 file key를 생성.

4. AES-256-CTS를 사용하여 파일 이름 복호화, AES-256-XTS를 사용하여 파일 내용 복호화.

 

 

[ FDE(Full-Disk Encryption)의 동작 과정 ]

1. user key와 salt를 scrypt 입력값으로 사용하여 32byte 중간 키 IK1을 생성

 

 

 

 

 

 

 

  • user key : pin/password/pattern
  • salt : dev/urandom에서 읽은 32byte 난수

 

 

 

 

 

 

*  Scrypt란?

    구조 :  PBKDF2 - SMIX - PBKDF2 

 

 

IK1 = scrypt(password, salt, N, r, p, dklen)

 

[input]

password

salt

N : CPU/memory cost (2의 배수)

r : blocksize parameter

p : 병렬화 (p <= (2^32)*hlen/MFLen)

dklen : 원하는 키 값 (dklen <= (2^32-1)*hlen)

 

[output]

IK1

 

 

 

 

2. IK1을 256byte(2048bit)까지 zero padding

 

 

 

 

00 || IK1 || 00 || 00 ... 00 || 00

( '00' 1byte || 'IK1' 32byte || '00' 223byte )  => 256byte

 

 

 

 

 

 

 

 

 

3.  패딩된 IK1에 HBK(hardware bound key)를 이용해 데이터를 서명해서 256byte IK2를 생성.

 

 

 

 

 

Android 5.0부터 Keymaster라는 하드웨어 지원 키 저장 기능 지원

- 암호화 키를 보호하기 위해 안전한 TEE 영역에서 구현

- dm-crypt는 FDE 수행을 담당하는 모듈

 

 

 

 

 

 

 

 

 

(1)  TEE 영역에서 Keymaster Key가 생성되면 HBK키를 사용하여 암호화됨.

           ->  ‘Key blob’ 암호화된 키 생성

           ->  안드로이드로 반환 

                    : 암호화되지 않은 영역인 ‘Crypto Footer’에 저장

(2) 안드로이드가 키를 사용하여 작업을 수행하려는 경우 ‘Key blob’를 keyMaster에 제공

 

(3) ‘key blob’를 HBK로 복호화

 

(4) 그 후 그 안에 포함된 RSA-2048 개인키로 데이터(패딩 된 IK1)를 서명

 

(5) 안드로이드로 반환  ->  IK2 생성 완료

 

 

 

4. 256byte IK2와 salt를 scrypt 입력값으로 사용해 32byte 중간키 IK3를 생성

 

 

 

 

 

처음 16byte를 KEK로 사용

마지막 16byte를 IV로 사용

 

 

 

 

 

 

 

 

 

5. 키 KEK와 초기화 벡터 IV를 사용하여 AES-CBC-128로 DEK를 암호화.

  - DEK : dev/urandom에서 읽은 32byte 난수

  - 암호화된 DEK는 “Crypto Footer”라는 암호화되지 않은 영역에 저장.

 

6. 암호화된 DEK로 DB를 암호화

    복호화하려면 암호화된 DEK를 복호화한 값으로 암호화된 DB를 복호화

+ Recent posts