"블로피시"의 두 판 사이의 차이
leejia1222 (토론 | 기여) (→알고리즘) |
|||
(사용자 3명의 중간 판 11개는 보이지 않습니다) | |||
1번째 줄: | 1번째 줄: | ||
− | '''블로피시'''(blowfish)는 1993년 [[브루스 슈나이어]](Bruce Schneier)가 [[데이터 암호화 표준]](DES; Data Encryption Standard)의 대안으로 개발한 [[대칭키 알고리즘]] | + | [[파일:블로피시 암호화.png |썸네일|300픽셀|오른쪽|'''블로피시''']] |
+ | |||
+ | '''블로피시'''(blowfish)는 1993년 [[브루스 슈나이어]](Bruce Schneier)가 [[데이터 암호화 표준]](DES; Data Encryption Standard)의 대안으로 개발한 [[대칭키]] [[알고리즘]]이다. 블로피시는 소프트웨어에서 양호한 암호화 속도를 제공한다. | ||
+ | |||
+ | ==개요== | ||
+ | 1993년 이전의 다른 암호 알고리즘은 사유 알고리즘이거나 정부와 산업체의 기밀 사항, 또는 특허를 가지고 있어서 일반인들과 개인이 사용하기에 용이란 일반 목적의 알고리즘이 존재하지 않았다. 이에 브루스 슈나이어는 일반 목적으로 사용가능한 블로피시 알고리즘을 개발했다. 블로피시는 비특허 알고리즘이기 때문에 퍼블릭 도메인에 속하며, 누구든 자유롭게 사용할 수 있다고 본인이 직접 밝혔다. 블로피시는 데이터 암호화 표준, 즉 DES와 국제 데이터 암호화 알고리즘을 대신하여 사용되는 암호화 알고리즘이다. 키의 길이는 32비트 내지 448비트의 가변 길이 키를 사용하는 비밀 키 블록 암호로, 키의 스케줄링이 복잡하여 [[S-box]]에 의존한다. 또, 블로피시는 현재까지도 비특허로써 모든 사용자에게 무료로 개방되어 있다. 미국 정부는 특별한 경우를 제외하면 40비트보다 큰 키를 사용하는 암호화 소프트웨어의 수출을 금지한다. 하지만 블로피시 알고리즘은 비특허이기 때문에 수출에 용이하다. 32비트 명령어 프로세서를 염두하며 설계되었고, DES보다 훨씬 빠르다. 소프트웨어에서 양호한 암호화 속도를 제공하지만, 현재는 고급암호화 표준이 더 주목을 받고 있다.<ref> 블로피시 위키백과 - https://ko.wikipedia.org/wiki/%EB%B8%94%EB%A1%9C%ED%94%BC%EC%8B%9C </ref><ref name='조석팔'> 조석팔, 〈[스마트폰 보안을 위한 통합된 타원곡선 암호와 블로피시 암호]〉, 《한국과학기술정보연구원》, 2016-06-21 </ref> | ||
− | == | + | 블로피시는 고유한 형태의 키 생성을 사용한다. 블로피시 루틴의 두 번째 부분은 최대 448비트의 단일 키를 크기가 4,168바이트인 하위 키 테이블로 변환하는 키 확장이다. 하위 키를 만들면 해커가 원래 키보다 더 많은 것을 해독해야만 하므로 보안이 더 강해진다. |
− | + | ||
+ | == 특징 == | ||
+ | === 장점 === | ||
+ | 블로피시는 몇 가지 장점이 있다. 그중 하나는 32비트 마이크로 프로세스에서 1바이트당 18클럭 사이클의 속도로 암호화하여 [[Khufu]], [[RC5]], [[DES]], [[IDEA]], [[Trip-DES]]와 비교했을 때, 그 속도가 매우 빠르다는 것이다. 간결성과 단순성 또한 블로피시의 장점이다. 블로피시는 5K 이내의 메모리에서 실행될 수 있으며, 간단한 구조는 구현이 쉽고 알고리즘의 강도 결정이 쉽다. 더불어 블로피시는 키의 길이가 가변적이어서 448비트 만큼 길어질 수 있으며 <math>2^{32}</math>를 법으로 수행되는 단어의 덧셈 연산과 비트 XOR, 두 개의 기본 연산이 가능하다. 이외에도 비특허 공개 알고리즘이라는 점, 사전 공격을 방어할 수 있어 보안성이 뛰어난다는 점 등이 블로피시의 특징이자 장점으로 꼽히고 있다. 이러한 장점들을 바탕으로 블로피시는 각종 온라인 게임의 [[패킷 암호화]], [[패킷 복호화]]에 주로 사용된다. | ||
+ | |||
+ | 블로피시 알고리즘은 과거에 기업이나 정부에서만 제한되는 것이 아니라 일반 목적으로 사용될 수 있도록 비특허 알고리즘으로 개발된 덕에 수출에 매우 용이하고 일반인들도 무료로 마음껏 사용할 수 있다는 장점이 있다. 또, 블로피시 알고리즘은 속도와 간결함, 키 사이즈 등에서 유연성을 갖고 있다. 블로피시는 가변 길이 키 구조의 64비트 알고리즘으로서, 주로 암호화 부분과 키 확장 부분으로 구성되어 있다. 암호키는 18비트로 변환되며 키를 확장할 때는 32비트 서브키로 변환되어 P어레이로 저장된다. 데이터 암호화는 16라운드 파이스텔 네트워크를 경유하여 처리되기 때문에 키가 자동화 파일 암호처럼 자주 변하지 않는 응용에서 적용하는 편이 적합하다. | ||
+ | |||
+ | 베나와 연구자들은 성능을 측정하기 위해 대칭 알고리즘인 DES, 3DES, AES, 블로피시 알고리즘을 구현했다. 결과를 에너지 소비와 실행 시간 측면에서 다른 대칭 알고리즘과 비교하여 블로피시가 월등하다는 내용이었다. 블로피시는 처리 시간과 자원 소비가 최적화되었으며, AES 알고리즘은 블록 사이즈의 증가로 프로세싱 능력과 시간이 더 필요했다. 3DES의 실행 시간은 3중 단계 암호화 때문에 DES보다 더 오래 걸리는 결과가 나왔다.<ref name='조석팔'></ref> | ||
+ | |||
+ | === 취약점 === | ||
+ | 블로피시는 높은 보안 수준과 빠른 속도를 갖고 있기 때문에 스마트폰 플랫폼에도 매우 적합한 알고리즘이라고 할 수 있다. 블로피시를 파괴할 수 있는 공격은 아직 없다. 그러나 블로피시는 키가 자주 변경되지 않을 때 최적의 성능을 보여주기 때문에, 블로피시 암호의 초기화는 스마트폰의 저장장치인 EEPROM이나 FLSASHPROM에 저장되므로 외부의 공격자들이 스마트폰의 메모리에서 서브키들을 훔쳐 갈 수 있어 키를 쉽게 끄집어낼 수 있다. 이러한 문제를 극복하기 위해 블로피시 암호화 및 복호화 과정에서 몇 가지 수정이 필요하다. | ||
− | + | 블로피시 4라운드는 2차 차분 공격에 취약하다. 취약 키의 경우에는 14라운드 블로피시를 의사 무작위 치환과 구별할 수 있다. | |
− | |||
− | |||
− | |||
− | ==구현== | + | == 적용 == |
+ | === 구현 === | ||
# 32비트 평문을 [[Parray]]의 첫 번째 구성 원소와 XOR 연산한다. | # 32비트 평문을 [[Parray]]의 첫 번째 구성 원소와 XOR 연산한다. | ||
− | # | + | # 결괏값을 BlowFish_F 함수로 연산한다. |
− | # | + | # 결괏값을 우측 값과 XOR 연산한다. |
− | # | + | # 결괏값을 좌측에 저장하고 1번의 연산값을 우측에 저장한다. |
# 1~4의 과정을 16회에 걸쳐 반복한다. | # 1~4의 과정을 16회에 걸쳐 반복한다. | ||
− | # 좌측 | + | # 좌측 연산 값을 P 18번째 원소과 XOR 연산하고 좌측에 저장한다. |
− | # 우측 | + | # 우측 연산 값을 P 17번째 원소와 XOR 연산하고 우측에 저장한다.<ref> empty1234, 〈[https://free1234.tistory.com/entry/BlowFish%EC%97%90-%EA%B4%80%ED%95%B4%EC%84%9C, BlowFish에 관해서]〉, 《티스토리》, 2010-04-25 </ref> |
+ | |||
+ | === 언어 === | ||
+ | ;비크립트(bcrypt) | ||
+ | :2016년, 정부의 후원을 받은 해커들이 최소 5억 명의 야후 계정 내역을 훔쳤다. 유출 사건 당시 도난당한 계정의 비밀번호가 비크립트로 암호화되어 있었는데, 이와 비슷한 시기에 트위터 또한 3,300만 명의 개인정보가 유출되었다. 비크립트는 해시 알고리즘으로, 해시는 해싱이라고도 부른다. 이러한 해시는 역행할 수 없게 되어 있어서 비밀번호를 저장하기에는 좋은 방법으로 평가받는데, 사용자가 만약 비밀번호와 동일한 내용을 입력하면 비밀번호는 해시 알고리즘을 거쳐서 이전에 이미 저장되어 있던 해시와 비교한다. 이는 실제 데이터베이스에 평문으로 저장하지 않아 사용자가 비밀번호를 바로 확인하는 방법을 제공하기 때문에 즉시 로그인 할 수 있다. 그러나 평문 비밀번호를 추정하기 위해서 시도하는 비밀번호 크래킹 공격에 대응하는 해시 알고리즘들이 모두 동일한 보호 역량을 지니고 있지는 않다. 오래된 MD5 같은 알고리즘은 별도의 보안 수단 없이 독자적으로 이행하면 크랙 하기 쉽다. 이와는 달리 비크립트는 강력한 알고리즘으로 되어 있어서 이론상으로는 해커들이 크래킹할 확률이 매우 낮다. 하지만 야후 유출 사건은 대부분의 비밀번호가 비크립트로 되어 있을 뿐, 전부가 비크립트 해시 알고리즘으로 보호받고 있는 것이 아니기 때문에, 얼마나 많은 비밀번호가 다른 알고리즘에 해시되어 있는가는 알 수 없었다. 또한, 자신의 계정 비밀번호가 비크립트에 해시되었는지 아닌지 알아낼 방법이 없었기 때문에 문제가 되었다.<ref> Lucian Constantin, 〈[https://news.zum.com/articles/33250026 야후 유출 사건에 대해 알아야 할 것과 해야 할 것]〉, 《아이티월드》, 2016-09-26 </ref> 비크립트는 유즈닉스(USENIX)에서, Niels Provos와 David Mazières가 설계한 암호 해시 함수이다. 블로피시 암호를 기반으로 설계되었으며, 패스워드 해시 알고리즘이라고도 부른다. 비크립트의 해시열의 기본 형태와 예시는 다음과 같다. | ||
+ | * 형태 | ||
+ | $2b$[cost]$[22 character salt][31 character hash] | ||
+ | |||
+ | * 예시 | ||
+ | $2a$10$N9qo8uLOickgx2ZMRZoMyeIjZAgcfl7p92ldGxad68LJZdL17lhWy | ||
+ | \__/\/ \____________________/\_____________________________/ | ||
+ | Alg Cost Salt Hash | ||
+ | |||
+ | === 데이터 암호화 === | ||
+ | 오픈 SSH에서 제공하는 블로피시 알고리즘을 이용해서 데이터를 암호화한다. 블로피시는 데이터의 암호화와 복호화를 위하여 동일한 키를 사용하는 대칭 알고리즘을 사용하고 있다. 암호화와 복호화를 위해 다른 키를 사용하는 RSA 알고리즘보다 비교적 사용하기가 간단한 동시에, 더 빠른 수행 능력을 보여준다는 것이 장점이다. 다음은 C 언어를 이용하여 블로피시 알고리즘을 사용하는 방법이다.<ref> yundream, 〈[https://www.joinc.co.kr/w/Site/Code/C/blowcrypt blowfish 알고리즘을 이용한 데이터 암호화]〉, 《디스커스》, 2004-03-30 </ref> | ||
+ | |||
+ | int keygen(struct mykey *key); | ||
+ | int cryptinit(); | ||
+ | int encrypt(unsigned char *data, size_t n,unsigned char *buf, struct mykey *lkey) | ||
+ | int decrypt(char *cryptmessage, unsigned char *outbuf, struct mykey *lkey, size_t n); | ||
+ | keygen은 암호화 및 복호화에 사용될 키를 생성한다. 이렇게 만들어진 키는 key에 복사되며, key는 다음과 같이 정의되어 있다. | ||
+ | struct mykey | ||
+ | { | ||
+ | unsigned char key[16]; | ||
+ | unsigned char vec[8]; | ||
+ | }; | ||
+ | cryptinit는 chiper를 초기화시키고, chiper는 전역으로 선언된다. 그후, encrypt는 주어진 data에서 n 바이트만큼을 암호화하여 buf에 저장한다. 암호화는 lkey를 통해서 이루어지고, 암호화된 데이터 크기를 반환한다. decrypt는 암호화된 데이터 cryptmessage에서 n 바이트만큼을 lkey를 이용해서 복호화한다. | ||
+ | {{글 보임 시작}} | ||
+ | struct mykey | ||
+ | { | ||
+ | unsigned char key[16]; | ||
+ | unsigned char vec[8]; | ||
+ | }; | ||
+ | |||
+ | // chipteer 초기화 | ||
+ | EVP_CIPHER_CTX ctx; | ||
+ | // 랜덤값을 얻는다. | ||
+ | // 필요에 따라서 /dev/random을 사용해도 된다. | ||
+ | int getrandom(char *rand, size_t size) | ||
+ | { | ||
+ | struct timeval tp; | ||
+ | int i = 0; | ||
+ | int ran; | ||
+ | gettimeofday(&tp, NULL); | ||
+ | srandom(tp.tv_sec | tp.tv_usec); | ||
+ | while(1) | ||
+ | { | ||
+ | if (!(size / (4*(i+1)))) break; | ||
+ | ran = random(); | ||
+ | memcpy(rand+(i*4), (void *)&ran, 4); | ||
+ | //memcpy(rand, (void *)&ran, 4); | ||
+ | i++; | ||
+ | } | ||
+ | } | ||
+ | |||
+ | // 키 생성기 | ||
+ | int keygen(struct mykey *lkey) | ||
+ | { | ||
+ | int i = 0; | ||
+ | char keyseed[24]; | ||
+ | getrandom(keyseed, 24); | ||
+ | memcpy((void *)lkey->key, keyseed, 16); | ||
+ | memcpy((void *)lkey->vec, keyseed+16, 8); | ||
+ | } | ||
+ | |||
+ | // chiper를 초기화 한다. | ||
+ | int cryptinit() | ||
+ | { | ||
+ | EVP_CIPHER_CTX_init(&ctx); | ||
+ | } | ||
+ | |||
+ | // data를 lkey를 이용해서 암호화 한다. | ||
+ | int encrypt(unsigned char *data, size_t n, unsigned char *buf, struct mykey *lkey) | ||
+ | { | ||
+ | int olen = 0, tlen, i; | ||
+ | char test[256] = {0x00,}; | ||
+ | printf("msg size is %d\n", n); | ||
+ | EVP_EncryptInit(&ctx, EVP_bf_cbc(), lkey->key, lkey->vec); | ||
+ | if (EVP_EncryptUpdate(&ctx, buf, &olen, data, n+4) != 1) | ||
+ | { | ||
+ | printf("Error in encrypt update\n"); | ||
+ | return -1; | ||
+ | } | ||
+ | |||
+ | |||
+ | if (EVP_EncryptFinal(&ctx, buf+olen, &tlen) != 1) | ||
+ | { | ||
+ | printf("Error in encrypt final\n"); | ||
+ | return -1; | ||
+ | } | ||
+ | printf("Encrypt size=%d, %d\n", olen, tlen); | ||
+ | EVP_CIPHER_CTX_cleanup(&ctx); | ||
+ | return olen+tlen; | ||
+ | } | ||
+ | |||
+ | // 주어진 암호화된 데이터를 lkey를 이용해서 복호화 한다. | ||
+ | int decrypt(char *cryptmessage, unsigned char *outbuf, struct mykey *lkey, size_t n) | ||
+ | { | ||
+ | int olen, tlen; | ||
+ | EVP_DecryptInit(&ctx, EVP_bf_cbc(), lkey->key, lkey->vec); | ||
+ | if(EVP_DecryptUpdate(&ctx, outbuf, &olen, (unsigned char *)cryptmessage, n) != 1) | ||
+ | { | ||
+ | printf("error in decrypt \n"); | ||
+ | return -1; | ||
+ | } | ||
+ | |||
+ | if(EVP_DecryptFinal(&ctx, outbuf+olen, &tlen) != 1) | ||
+ | { | ||
+ | printf("error in decrypt final\n"); | ||
+ | return -1; | ||
+ | } | ||
+ | return olen+tlen; | ||
+ | } | ||
+ | |||
+ | int main() | ||
+ | { | ||
+ | int i; | ||
+ | struct mykey lkey; | ||
+ | unsigned char buf[256] = {0x00,}; | ||
+ | unsigned char buf2[256] = {0x00,}; | ||
+ | unsigned char hello[256] = {0x00,}; | ||
+ | unsigned char hello2[256] = {0x00,}; | ||
+ | int n; | ||
+ | |||
+ | strcpy((char *)hello, "hellow world test test test\n"); | ||
+ | strcpy((char *)hello2, "안녕하세요 세상들아..\n"); | ||
+ | keygen(&lkey); | ||
+ | cryptinit(); | ||
+ | n = encrypt(hello, strlen((char *)hello), buf, &lkey); | ||
+ | decrypt((char *)buf, buf2, &lkey, n); | ||
+ | printf("%s\n", buf2); | ||
+ | printf("===================="); | ||
+ | |||
+ | n = encrypt(hello2, strlen((char *)hello2), buf, &lkey); | ||
+ | decrypt((char *)buf, buf2, &lkey, n); | ||
+ | printf("%s\n", buf2); | ||
+ | } | ||
+ | {{글 보임 끝}} | ||
− | == | + | == 의사코드 == |
− | + | 01 uint32_t P[18]; | |
+ | 02 uint32_t S[4][256]; | ||
+ | 03 | ||
+ | 04 uint32_t f (uint32_t x) { | ||
+ | 05 uint32_t h = S[0][x >> 24] + S[1][x >> 16 & 0xff]; | ||
+ | 06 return ( h ^ S[2][x >> 8 & 0xff] ) + S[3][x & 0xff]; | ||
+ | 07 } | ||
+ | 08 | ||
+ | 09 void encrypt (uint32_t & L, uint32_t & R) { | ||
+ | 10 for (int i=0 ; i<16 ; i += 2) { | ||
+ | 11 L ^= P[i]; | ||
+ | 12 R ^= f(L); | ||
+ | 13 R ^= P[i+1]; | ||
+ | 14 L ^= f(R); | ||
+ | 15 } | ||
+ | 16 L ^= P[16]; | ||
+ | 17 R ^= P[17]; | ||
+ | 18 swap (L, R); | ||
+ | 19 } | ||
+ | 20 | ||
+ | 21 void decrypt (uint32_t & L, uint32_t & R) { | ||
+ | 22 for (int i=16 ; i > 0 ; i -= 2) { | ||
+ | 22 L ^= P[i+1]; | ||
+ | 23 R ^= f(L); | ||
+ | 24 R ^= P[i]; | ||
+ | 25 L ^= f(R); | ||
+ | 26 } | ||
+ | 27 L ^= P[1]; | ||
+ | 28 R ^= P[0]; | ||
+ | 29 swap (L, R); | ||
+ | 30 } | ||
+ | 31 | ||
+ | 32 // ... | ||
+ | 33 // initializing the P-array and S-boxes with values derived from pi; omitted in the example | ||
+ | 34 // ... | ||
+ | 35 { | ||
+ | 36 for (int i=0 ; i<18 ; ++i) | ||
+ | 37 P[i] ^= key[i % keylen]; | ||
+ | 38 uint32_t L = 0, R = 0; | ||
+ | 39 for (int i=0 ; i<18 ; i+=2) { | ||
+ | 40 encrypt (L, R); | ||
+ | 41 P[i] = L; P[i+1] = R; | ||
+ | 42 } | ||
+ | 43 for (int i=0 ; i<4 ; ++i) | ||
+ | 44 for (int j=0 ; j<256; j+=2) { | ||
+ | 45 encrypt (L, R); | ||
+ | 46 S[i][j] = L; S[i][j+1] = R; | ||
+ | 47 } | ||
+ | 48 } | ||
{{각주}} | {{각주}} | ||
− | ==참고자료== | + | == 참고자료 == |
+ | * 블로피시 위키백과 - https://ko.wikipedia.org/wiki/%EB%B8%94%EB%A1%9C%ED%94%BC%EC%8B%9C | ||
+ | * yundream, 〈[https://www.joinc.co.kr/w/Site/Code/C/blowcrypt blowfish 알고리즘을 이용한 데이터 암호화]〉, 《디스커스》, 2004-03-30 | ||
+ | * empty1234, 〈[https://free1234.tistory.com/entry/BlowFish%EC%97%90-%EA%B4%80%ED%95%B4%EC%84%9C, BlowFish에 관해서]〉, 《티스토리》, 2010-04-25 | ||
+ | * 조석팔, 〈[스마트폰 보안을 위한 통합된 타원곡선 암호와 블로피시 암호]〉, 《한국과학기술정보연구원》, 2016-06-21 | ||
+ | * Lucian Constantin, 〈[https://news.zum.com/articles/33250026 야후 유출 사건에 대해 알아야 할 것과 해야 할 것]〉, 《아이티월드》, 2016-09-26 | ||
== 같이 보기 == | == 같이 보기 == | ||
+ | * [[암호 알고리즘]] | ||
+ | * [[DES]] | ||
+ | * [[대칭키]] | ||
− | {{알고리즘|검토 필요}} | + | {{암호 알고리즘|검토 필요}} |
2020년 8월 28일 (금) 16:11 기준 최신판
블로피시(blowfish)는 1993년 브루스 슈나이어(Bruce Schneier)가 데이터 암호화 표준(DES; Data Encryption Standard)의 대안으로 개발한 대칭키 알고리즘이다. 블로피시는 소프트웨어에서 양호한 암호화 속도를 제공한다.
개요[편집]
1993년 이전의 다른 암호 알고리즘은 사유 알고리즘이거나 정부와 산업체의 기밀 사항, 또는 특허를 가지고 있어서 일반인들과 개인이 사용하기에 용이란 일반 목적의 알고리즘이 존재하지 않았다. 이에 브루스 슈나이어는 일반 목적으로 사용가능한 블로피시 알고리즘을 개발했다. 블로피시는 비특허 알고리즘이기 때문에 퍼블릭 도메인에 속하며, 누구든 자유롭게 사용할 수 있다고 본인이 직접 밝혔다. 블로피시는 데이터 암호화 표준, 즉 DES와 국제 데이터 암호화 알고리즘을 대신하여 사용되는 암호화 알고리즘이다. 키의 길이는 32비트 내지 448비트의 가변 길이 키를 사용하는 비밀 키 블록 암호로, 키의 스케줄링이 복잡하여 S-box에 의존한다. 또, 블로피시는 현재까지도 비특허로써 모든 사용자에게 무료로 개방되어 있다. 미국 정부는 특별한 경우를 제외하면 40비트보다 큰 키를 사용하는 암호화 소프트웨어의 수출을 금지한다. 하지만 블로피시 알고리즘은 비특허이기 때문에 수출에 용이하다. 32비트 명령어 프로세서를 염두하며 설계되었고, DES보다 훨씬 빠르다. 소프트웨어에서 양호한 암호화 속도를 제공하지만, 현재는 고급암호화 표준이 더 주목을 받고 있다.[1][2]
블로피시는 고유한 형태의 키 생성을 사용한다. 블로피시 루틴의 두 번째 부분은 최대 448비트의 단일 키를 크기가 4,168바이트인 하위 키 테이블로 변환하는 키 확장이다. 하위 키를 만들면 해커가 원래 키보다 더 많은 것을 해독해야만 하므로 보안이 더 강해진다.
특징[편집]
장점[편집]
블로피시는 몇 가지 장점이 있다. 그중 하나는 32비트 마이크로 프로세스에서 1바이트당 18클럭 사이클의 속도로 암호화하여 Khufu, RC5, DES, IDEA, Trip-DES와 비교했을 때, 그 속도가 매우 빠르다는 것이다. 간결성과 단순성 또한 블로피시의 장점이다. 블로피시는 5K 이내의 메모리에서 실행될 수 있으며, 간단한 구조는 구현이 쉽고 알고리즘의 강도 결정이 쉽다. 더불어 블로피시는 키의 길이가 가변적이어서 448비트 만큼 길어질 수 있으며 를 법으로 수행되는 단어의 덧셈 연산과 비트 XOR, 두 개의 기본 연산이 가능하다. 이외에도 비특허 공개 알고리즘이라는 점, 사전 공격을 방어할 수 있어 보안성이 뛰어난다는 점 등이 블로피시의 특징이자 장점으로 꼽히고 있다. 이러한 장점들을 바탕으로 블로피시는 각종 온라인 게임의 패킷 암호화, 패킷 복호화에 주로 사용된다.
블로피시 알고리즘은 과거에 기업이나 정부에서만 제한되는 것이 아니라 일반 목적으로 사용될 수 있도록 비특허 알고리즘으로 개발된 덕에 수출에 매우 용이하고 일반인들도 무료로 마음껏 사용할 수 있다는 장점이 있다. 또, 블로피시 알고리즘은 속도와 간결함, 키 사이즈 등에서 유연성을 갖고 있다. 블로피시는 가변 길이 키 구조의 64비트 알고리즘으로서, 주로 암호화 부분과 키 확장 부분으로 구성되어 있다. 암호키는 18비트로 변환되며 키를 확장할 때는 32비트 서브키로 변환되어 P어레이로 저장된다. 데이터 암호화는 16라운드 파이스텔 네트워크를 경유하여 처리되기 때문에 키가 자동화 파일 암호처럼 자주 변하지 않는 응용에서 적용하는 편이 적합하다.
베나와 연구자들은 성능을 측정하기 위해 대칭 알고리즘인 DES, 3DES, AES, 블로피시 알고리즘을 구현했다. 결과를 에너지 소비와 실행 시간 측면에서 다른 대칭 알고리즘과 비교하여 블로피시가 월등하다는 내용이었다. 블로피시는 처리 시간과 자원 소비가 최적화되었으며, AES 알고리즘은 블록 사이즈의 증가로 프로세싱 능력과 시간이 더 필요했다. 3DES의 실행 시간은 3중 단계 암호화 때문에 DES보다 더 오래 걸리는 결과가 나왔다.[2]
취약점[편집]
블로피시는 높은 보안 수준과 빠른 속도를 갖고 있기 때문에 스마트폰 플랫폼에도 매우 적합한 알고리즘이라고 할 수 있다. 블로피시를 파괴할 수 있는 공격은 아직 없다. 그러나 블로피시는 키가 자주 변경되지 않을 때 최적의 성능을 보여주기 때문에, 블로피시 암호의 초기화는 스마트폰의 저장장치인 EEPROM이나 FLSASHPROM에 저장되므로 외부의 공격자들이 스마트폰의 메모리에서 서브키들을 훔쳐 갈 수 있어 키를 쉽게 끄집어낼 수 있다. 이러한 문제를 극복하기 위해 블로피시 암호화 및 복호화 과정에서 몇 가지 수정이 필요하다.
블로피시 4라운드는 2차 차분 공격에 취약하다. 취약 키의 경우에는 14라운드 블로피시를 의사 무작위 치환과 구별할 수 있다.
적용[편집]
구현[편집]
- 32비트 평문을 Parray의 첫 번째 구성 원소와 XOR 연산한다.
- 결괏값을 BlowFish_F 함수로 연산한다.
- 결괏값을 우측 값과 XOR 연산한다.
- 결괏값을 좌측에 저장하고 1번의 연산값을 우측에 저장한다.
- 1~4의 과정을 16회에 걸쳐 반복한다.
- 좌측 연산 값을 P 18번째 원소과 XOR 연산하고 좌측에 저장한다.
- 우측 연산 값을 P 17번째 원소와 XOR 연산하고 우측에 저장한다.[3]
언어[편집]
- 비크립트(bcrypt)
- 2016년, 정부의 후원을 받은 해커들이 최소 5억 명의 야후 계정 내역을 훔쳤다. 유출 사건 당시 도난당한 계정의 비밀번호가 비크립트로 암호화되어 있었는데, 이와 비슷한 시기에 트위터 또한 3,300만 명의 개인정보가 유출되었다. 비크립트는 해시 알고리즘으로, 해시는 해싱이라고도 부른다. 이러한 해시는 역행할 수 없게 되어 있어서 비밀번호를 저장하기에는 좋은 방법으로 평가받는데, 사용자가 만약 비밀번호와 동일한 내용을 입력하면 비밀번호는 해시 알고리즘을 거쳐서 이전에 이미 저장되어 있던 해시와 비교한다. 이는 실제 데이터베이스에 평문으로 저장하지 않아 사용자가 비밀번호를 바로 확인하는 방법을 제공하기 때문에 즉시 로그인 할 수 있다. 그러나 평문 비밀번호를 추정하기 위해서 시도하는 비밀번호 크래킹 공격에 대응하는 해시 알고리즘들이 모두 동일한 보호 역량을 지니고 있지는 않다. 오래된 MD5 같은 알고리즘은 별도의 보안 수단 없이 독자적으로 이행하면 크랙 하기 쉽다. 이와는 달리 비크립트는 강력한 알고리즘으로 되어 있어서 이론상으로는 해커들이 크래킹할 확률이 매우 낮다. 하지만 야후 유출 사건은 대부분의 비밀번호가 비크립트로 되어 있을 뿐, 전부가 비크립트 해시 알고리즘으로 보호받고 있는 것이 아니기 때문에, 얼마나 많은 비밀번호가 다른 알고리즘에 해시되어 있는가는 알 수 없었다. 또한, 자신의 계정 비밀번호가 비크립트에 해시되었는지 아닌지 알아낼 방법이 없었기 때문에 문제가 되었다.[4] 비크립트는 유즈닉스(USENIX)에서, Niels Provos와 David Mazières가 설계한 암호 해시 함수이다. 블로피시 암호를 기반으로 설계되었으며, 패스워드 해시 알고리즘이라고도 부른다. 비크립트의 해시열의 기본 형태와 예시는 다음과 같다.
- 형태
$2b$[cost]$[22 character salt][31 character hash]
- 예시
$2a$10$N9qo8uLOickgx2ZMRZoMyeIjZAgcfl7p92ldGxad68LJZdL17lhWy \__/\/ \____________________/\_____________________________/ Alg Cost Salt Hash
데이터 암호화[편집]
오픈 SSH에서 제공하는 블로피시 알고리즘을 이용해서 데이터를 암호화한다. 블로피시는 데이터의 암호화와 복호화를 위하여 동일한 키를 사용하는 대칭 알고리즘을 사용하고 있다. 암호화와 복호화를 위해 다른 키를 사용하는 RSA 알고리즘보다 비교적 사용하기가 간단한 동시에, 더 빠른 수행 능력을 보여준다는 것이 장점이다. 다음은 C 언어를 이용하여 블로피시 알고리즘을 사용하는 방법이다.[5]
int keygen(struct mykey *key); int cryptinit(); int encrypt(unsigned char *data, size_t n,unsigned char *buf, struct mykey *lkey) int decrypt(char *cryptmessage, unsigned char *outbuf, struct mykey *lkey, size_t n);
keygen은 암호화 및 복호화에 사용될 키를 생성한다. 이렇게 만들어진 키는 key에 복사되며, key는 다음과 같이 정의되어 있다.
struct mykey { unsigned char key[16]; unsigned char vec[8]; };
cryptinit는 chiper를 초기화시키고, chiper는 전역으로 선언된다. 그후, encrypt는 주어진 data에서 n 바이트만큼을 암호화하여 buf에 저장한다. 암호화는 lkey를 통해서 이루어지고, 암호화된 데이터 크기를 반환한다. decrypt는 암호화된 데이터 cryptmessage에서 n 바이트만큼을 lkey를 이용해서 복호화한다.
이 글을 숨기려면 오른쪽 '접기' 버튼을 누르세요! |
---|
struct mykey { unsigned char key[16]; unsigned char vec[8]; }; // chipteer 초기화 EVP_CIPHER_CTX ctx; // 랜덤값을 얻는다. // 필요에 따라서 /dev/random을 사용해도 된다. int getrandom(char *rand, size_t size) { struct timeval tp; int i = 0; int ran; gettimeofday(&tp, NULL); srandom(tp.tv_sec | tp.tv_usec); while(1) { if (!(size / (4*(i+1)))) break; ran = random(); memcpy(rand+(i*4), (void *)&ran, 4); //memcpy(rand, (void *)&ran, 4); i++; } } // 키 생성기 int keygen(struct mykey *lkey) { int i = 0; char keyseed[24]; getrandom(keyseed, 24); memcpy((void *)lkey->key, keyseed, 16); memcpy((void *)lkey->vec, keyseed+16, 8); } // chiper를 초기화 한다. int cryptinit() { EVP_CIPHER_CTX_init(&ctx); } // data를 lkey를 이용해서 암호화 한다. int encrypt(unsigned char *data, size_t n, unsigned char *buf, struct mykey *lkey) { int olen = 0, tlen, i; char test[256] = {0x00,}; printf("msg size is %d\n", n); EVP_EncryptInit(&ctx, EVP_bf_cbc(), lkey->key, lkey->vec); if (EVP_EncryptUpdate(&ctx, buf, &olen, data, n+4) != 1) { printf("Error in encrypt update\n"); return -1; } if (EVP_EncryptFinal(&ctx, buf+olen, &tlen) != 1) { printf("Error in encrypt final\n"); return -1; } printf("Encrypt size=%d, %d\n", olen, tlen); EVP_CIPHER_CTX_cleanup(&ctx); return olen+tlen; } // 주어진 암호화된 데이터를 lkey를 이용해서 복호화 한다. int decrypt(char *cryptmessage, unsigned char *outbuf, struct mykey *lkey, size_t n) { int olen, tlen; EVP_DecryptInit(&ctx, EVP_bf_cbc(), lkey->key, lkey->vec); if(EVP_DecryptUpdate(&ctx, outbuf, &olen, (unsigned char *)cryptmessage, n) != 1) { printf("error in decrypt \n"); return -1; } if(EVP_DecryptFinal(&ctx, outbuf+olen, &tlen) != 1) { printf("error in decrypt final\n"); return -1; } return olen+tlen; } int main() { int i; struct mykey lkey; unsigned char buf[256] = {0x00,}; unsigned char buf2[256] = {0x00,}; unsigned char hello[256] = {0x00,}; unsigned char hello2[256] = {0x00,}; int n; strcpy((char *)hello, "hellow world test test test\n"); strcpy((char *)hello2, "안녕하세요 세상들아..\n"); keygen(&lkey); cryptinit(); n = encrypt(hello, strlen((char *)hello), buf, &lkey); decrypt((char *)buf, buf2, &lkey, n); printf("%s\n", buf2); printf("===================="); n = encrypt(hello2, strlen((char *)hello2), buf, &lkey); decrypt((char *)buf, buf2, &lkey, n); printf("%s\n", buf2); } |
의사코드[편집]
01 uint32_t P[18]; 02 uint32_t S[4][256]; 03 04 uint32_t f (uint32_t x) { 05 uint32_t h = S[0][x >> 24] + S[1][x >> 16 & 0xff]; 06 return ( h ^ S[2][x >> 8 & 0xff] ) + S[3][x & 0xff]; 07 } 08 09 void encrypt (uint32_t & L, uint32_t & R) { 10 for (int i=0 ; i<16 ; i += 2) { 11 L ^= P[i]; 12 R ^= f(L); 13 R ^= P[i+1]; 14 L ^= f(R); 15 } 16 L ^= P[16]; 17 R ^= P[17]; 18 swap (L, R); 19 } 20 21 void decrypt (uint32_t & L, uint32_t & R) { 22 for (int i=16 ; i > 0 ; i -= 2) { 22 L ^= P[i+1]; 23 R ^= f(L); 24 R ^= P[i]; 25 L ^= f(R); 26 } 27 L ^= P[1]; 28 R ^= P[0]; 29 swap (L, R); 30 } 31 32 // ... 33 // initializing the P-array and S-boxes with values derived from pi; omitted in the example 34 // ... 35 { 36 for (int i=0 ; i<18 ; ++i) 37 P[i] ^= key[i % keylen]; 38 uint32_t L = 0, R = 0; 39 for (int i=0 ; i<18 ; i+=2) { 40 encrypt (L, R); 41 P[i] = L; P[i+1] = R; 42 } 43 for (int i=0 ; i<4 ; ++i) 44 for (int j=0 ; j<256; j+=2) { 45 encrypt (L, R); 46 S[i][j] = L; S[i][j+1] = R; 47 } 48 }
각주[편집]
- ↑ 블로피시 위키백과 - https://ko.wikipedia.org/wiki/%EB%B8%94%EB%A1%9C%ED%94%BC%EC%8B%9C
- ↑ 2.0 2.1 조석팔, 〈[스마트폰 보안을 위한 통합된 타원곡선 암호와 블로피시 암호]〉, 《한국과학기술정보연구원》, 2016-06-21
- ↑ empty1234, 〈BlowFish에 관해서〉, 《티스토리》, 2010-04-25
- ↑ Lucian Constantin, 〈야후 유출 사건에 대해 알아야 할 것과 해야 할 것〉, 《아이티월드》, 2016-09-26
- ↑ yundream, 〈blowfish 알고리즘을 이용한 데이터 암호화〉, 《디스커스》, 2004-03-30
참고자료[편집]
- 블로피시 위키백과 - https://ko.wikipedia.org/wiki/%EB%B8%94%EB%A1%9C%ED%94%BC%EC%8B%9C
- yundream, 〈blowfish 알고리즘을 이용한 데이터 암호화〉, 《디스커스》, 2004-03-30
- empty1234, 〈BlowFish에 관해서〉, 《티스토리》, 2010-04-25
- 조석팔, 〈[스마트폰 보안을 위한 통합된 타원곡선 암호와 블로피시 암호]〉, 《한국과학기술정보연구원》, 2016-06-21
- Lucian Constantin, 〈야후 유출 사건에 대해 알아야 할 것과 해야 할 것〉, 《아이티월드》, 2016-09-26
같이 보기[편집]