[프로젝트] Smart Card protocol 분석
이번 학기에 Smart Card 에 대한 부채널 분석을 진행하게 되면서 해당 카드의 동작 프로토콜을 분석해야한다.
0. APDU
Application Protocol Data Unit의 준말로 응용 계층에서 대등한 응용 실체 간에 주고 받는 데이터의 단위로 응용 프로토콜 제어 정보와 응용 계층 사용자 데이터를 포함한다. [출처 : 위치백과]
간단하게 smart card에서 쓰이는 명령어를 공부한다고 보면된다. (마치 C언어 처럼)
1. APDU 포멧
1.1 명령어 포멧
명령어 APDU 아래 그림과 같다.
cla와 ins p1 p2 부분은 필수 적으로 들어가야 하고 나머지 부분은 옵션으로 들어가도 된다.
- CLA : Class of Instruction
- INS : Instruction Code
- P1 : Instruction Parameter 1 (00 ~ FF)
- P2 : Instruction Parameter 2 (00 ~ FF)
- LC : 명령어 데이터 필드내 존재하는 바이트수
- DATA : 보내는 바이트
- LE : 최대 바이트 수
1.2 응답 APDU 포맷
- Data : 카드로부터의 응답 데이터
- SW1 : Command Processing Statue
- SW2 : Command Processing Qualifier
2. 세부 포맷
2.1 세부 명령어 포맷
- B0 : Read Binary
- D6 : Updatae Binary
- 0E : Erase Binary
- B2 : Read Record
- E2 : Append Record
- DC : Update Record
- CA : Get Data
- DA : Put Data
- A4 : Select File
- 20 : Verify
- 88 : Internal Authenticate
- E4 : Get Encipher
- C0 : Get Response
사실 많긴한데 두번째 header에는 이정도만 넣으면 된다.
2.2 SW1 SW2 포맷
다음과 같다. SW1과 SW2는 APDU로 Smart Card의 명령어를 보냈을 경우, 응답 메시지 형식인데, 응답 형식을 통해서 명령의 오류등을 확인할 수 있다.
예시로 다음의 사진을 코드 실행결과를 보면 sw1 = 0x61 , sw2 = 0x33이면 정상처리 된 상황이고, 0x33 바이트를 받을 수 있다는 것이다.
3. 명령어 조합
기본적으로 smart card가 동작을 하려면, 카드리더기랑 test용 카드가 필요하다.
실험환경은 Wsl 환경과 anaconda 환경에서 진행을 했다. 카드 리더기는 LEIA Borad를 사용했고 test용 카드는 내 학생증 카드를 이용했다.
나중에 파형도 같이 모아서 다음의 보드를 사용했다. 다음에는 이 보드 사용법도 포스팅할 생각이다.
필요 python module 1. smartleia 2. binascii |
1. selectdf
apdu_selectdf = sl.create_APDU_from_bytes(binascii.unhexlify('00A4040007D4100000030001'))
다음의 명령어를 해석하면
APDU(cla=0x0, ins=0xa4, p1=0x4, p2=0x0, lc=7, le=0, send_le=0, data=[212, 16, 0, 0, 3, 0, 1])
다음과 같다. 다음 APDU를 smartleia의 send_APDU를 이용해서 card에 신호를 주어야한다.
resp = reader.send_APDU(apdu_selectdf)
print(resp)
print(resp.sw2)
[출력]
RESP(sw1=0x61, sw2=0x33, le=0x0)
delta_t=28172 microseconds, delta_t_answer=28082 microseconds
51
sw1이 0x61이면 정상반응이고 0x33의 응답을 받을 수 있다는 뜻이다. 이게 사실 처음에는 resp가 명령어를 보내면 바로 리턴이 될줄 알았는데 조금 당황스러웠다.
그래서 해당 명령어의 Card의 진짜 반응을 보기위해서는 다음의 위의 명령어를 친다음에, 새로 반응을 봐야한다.
apdu_getresponse_selectdf = sl.create_APDU_from_bytes(binascii.unhexlify('00C0000033'))
resp = reader.send_APDU(apdu_getresponse_selectdf)
print(resp)
print('Response: ', ''.join(['%02X' % i for i in resp.data]))
[출력]
RESP(sw1=0x90, sw2=0x00, le=0x33, data=[111, 49, 176, 47, 0, 16, 1, 8, 16, 16, 0, 22, 105, 119, 65, 88, 5, 21, 17, 52, 56, 32, 24, 2, 7, 32, 35, 2, 6, 4, 0, 0, 7, 161, 32, 64, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0])
delta_t=77326 microseconds, delta_t_answer=77237 microseconds
Response: 6F31B02F0010010810100016697741580515113438201802072023020604000007A12040000000000000000000000000000000
다음의 response를 받아볼 수 있다.
그다음 내 학생증 카드의 암호화 기능을 사용하려면 ins의 Get Encipher를 사용해서 암호화를 해보자
10000을 암호화 해보자 = > 0x2710
apdu_sendmoney = sl.create_APDU_from_bytes(binascii.unhexlify('904000000400002710'))
resp = reader.send_APDU(apdu_sendmoney)
print(resp)
print('Response: ', ''.join(['%02X' % i for i in resp.data]))
[출력]
RESP(sw1=0x61, sw2=0x1E, le=0x0)
delta_t=46941 microseconds, delta_t_answer=46851 microseconds
Response:
역시 0x61이 와서 정상동작했다. 다시 Get Response로 확인해보면
apdu_getresponse_getcipher = sl.create_APDU_from_bytes(binascii.unhexlify('00C000001E'))
print(apdu_getresponse_getcipher)
resp = reader.send_APDU(apdu_getresponse_getcipher)
print(resp)
print('Response: ', ''.join(['%02X' % i for i in resp.data]))
[출력]
APDU(cla=0x0, ins=0xc0, p1=0x0, p2=0x0, lc=0, le=30, send_le=1)
RESP(sw1=0x90, sw2=0x00, le=0x1e, data=[16, 0, 0, 2, 38, 8, 16, 16, 0, 22, 105, 119, 65, 88, 0, 0, 0, 22, 98, 114, 222, 10, 1, 254, 179, 65, 114, 12, 153, 26])
delta_t=50543 microseconds, delta_t_answer=50452 microseconds
Response: 1000000226081010001669774158000000166272DE0A01FEB341720C991A
다음처럼 반응이 오는 것을 알 수 있다.