DES

Programming/Security | 2011. 11. 29. 22:38
Posted by 신이내린프로그래머

1. DES(Data Encryption Standard)  

    DES는 평문을 64비트로 나눠 56비트의 키를 이용해 다시 64비트의 암호문을 만들어 내는 알고리즘이다.(대칭형 블럭 암호)

     DES 알고리즘의 모습은 대체로 다음과 같다.  64비트의 평문이 16라운드를 거쳐 
    64비트의
 암호문을 나오게 하는 것이다

   INPUT :  64비트의 평문과 키 스케줄을 거친 64비트의 키가  입력된다.  OUTPUT : 64비트의 암호문이 나온다. 

    ① 먼저 64비트의 평문이 첫 라운드를 거치기 전에 IP(initial permutation)를 거친다. 
    ② IP를 거친 뒤 평문은 첫 번째 라운드에 들어가게 되는데, 좌우 각각 
       32비트(Lo,Ro)로 나눠서
 들어간다. 
    ③ 이제 오른쪽 32비트는 키 스케줄에 의해 나온 첫 번째 48비트 키와 F함수에
       들어가고 F함수
는 32비트를 내뱉는다.
    ④ F함수에서 나온 32비트는 2번의 왼쪽 32비트와 XOR연산을 거치게 된다.
    ⑤ 첫 라운드의 오른쪽 32비트는 다음 라운드의 왼쪽 32비트로 들어가고, XOR
       연산을 거친 32
트는 다음 라운드의 오른쪽 32비트로 들어가게 된다. 
    ⑥ 두 번째 라운드부터 16번째 라운드까지 첫 번째 라운드와 같은 방식으로 
       이루어 진다. 
    ⑦ 마지막 라운드를 거친 뒤 IP-1에 들어가게 되는데 이 때는 좌우가 
       바뀌어서( R 16,L16)로 
들어간다. 
    ⑧ IP-1를 거친 것이 64비트의 암호문이다. 

   F함수에는 오른쪽의 32비트 텍스트와 키 스케줄을 거친 키가 들어간다. 

    ① 오른쪽 32비트는 E(expansion)를 거치게 되어 48비트가 된다.
    ② 이 48비트는 키(48비트)와 XOR연산을 하게 된다. 
    ③ XOR연산의 결과로 나온 48비트는 6비트씩 잘려서 8개의 S-box에 들어가게 된다. 
    ④ 각각의 6비트는 S-box를 거친 뒤 4비트가 되어서 나오게 된다. 
    ⑤ 4비트씩 8개가 모여 다시 32비트를 이루게 된다. 
    ⑥ 이 32비트는 P(permutation)를 거쳐서 F함수의 결과(32비트)를 내 놓는다. 

     <키 스케줄>
    사용자는 56비트의 키를 입력하는데 이것에 8비트의 parity bits가 포함되어 키 
   스케줄에는 
모두 64비트의 키가 들어간다.  
   parity bits는 키 사이즈를 64에서 56 비트로 줄여준다.

   키 스케줄을 거친 뒤 16개의 48비트 키가 생성되는데 그 과정은 다음과 같다. 

    ① 키 스케줄을 하기 전에 쉬프트 횟수를 정의하는데 1, 2, 9, 16번째는 1번, 
     나머지는 2번씩
이다.  이것으로 총 28번의 쉬프트가 이루어진다.

    ② 먼저 키는 PC1박스를 거치게 된다.  PC1박스를 거친 후엔 두 부분(각각 28비트)으로 나뉘게 된다.

    ③ 이제 각각의 두 부분은 위에서 정의한 데로 1번째에는 1번의 왼쪽 쉬프트를, 두 번째에는 1번의 왼쪽 쉬프트를, 세 번째에는 2번의 왼쪽 쉬프트를 .....16번째에는 1번의 왼쪽 쉬프트를 하게 된다.

    ④ 첫 번째 키는 1번째 쉬프트를 했을 때 두 부분을 합쳐서 PC2박스를 통과시켜 나오는 48비트 이다.

    ⑤ 두 번째 키는 2번째 쉬프트를 한 후 첫 번째와 같이 두 부분을 합쳐 PC2박스를 통과시켜 나오는 48비트이다.

    ⑥ 이런 식으로 16번째까지 모두 16개의 키가 생성되게 된다.

   2. 다중 DES

      - Double DES

    DES 알고리즘 자체는 변형시키지 않고 DES의 안전성을 증대시키기 위한 방법 중의 하나로

    2개의 다른키로 2번 암호화를 수행한다.

  2중 DES의 암호화

  2중 DES의 복호화

    2개의 서로 다른 키가 적용되어졌기 때문에 2중 DES의 유효한 키는 56 ×112비트이다.

    이 경우의 모든 가능한 키를 시도해보는 횟수는 2112 번 작업이 소요된다.
    관측된  한 쌍의 평문과 암호문(m,c)가 주어졌을 때

 

 관계가 성립하며 아래의 관계식을 구할 수가 있다.

    m에 2개의 모든 가능한 k1을 적용하여 암호화시킨 x값을 정렬하여 보관한다.다음에는 역시 256 개의 모든 가능한 k2를 c에 적용하여 복호화한다. 
    복호화되는 가각의 값을 저장된 테이블에 있는 값과 비교하여 일치하는 항목에 대한 k1과 k2를 선정하여 이것을 하나의 평문과 암호문의 쌍에 적용시켜 검증한다. 

    이 암호분석에 소용되는 작업량은 암호화와 복호화에 2·256 그리고 n개의 값을 정렬하는데


의 작업이 요구되어 전반적으로 263 로 정도의 작업이 소요되어 단일

         DES에 비해 크게 증대되지 못했다. 따라서 "중간충돌" 공격을 방지 할 수 있는 
         Triple 
DES가  제시 되어졌다.

    - Triple DES

             "중간충돌" 공격을 방지할 수 있다.

         

   3. DES 운영방식

       1) ECB(Electronic CodeBook)

         ECB 모드는 DES 암호 방식의 사용 방식 중 가장 간단한 방식으로 평문을 
        64비트씩 나누어 
암호화하는 방식이다. 평문을 64비트씩 나눌 때 마지막 블록이
        64비트가 되지 않을 때는 임
의의 약속된 비트 모양을 패딩(padding)하게 된다. 
       64비트 블록의 평문을 그림 4.6과 같이 
암호화한다.

    이 방식은 동일한 평문 블록 모양에 따라 항상 동일한 암호문이 출력되므로 암호 해독자들

    의 해독 가능성을 높게 만든다. 즉, 문서의 종류에 따라 동일한 문서 모양을 갖고 있으므로 암호문 단독 공격의 가능성을 높게 해준다. 따라서 64비트 길이의 평문 암호화에 유용하게 사용될 수 있으므로 DES 암호 방식의 키 암호화에 사용할 수 있다.

       2) CBC(Cipher Block Chaining)

    DES 암호 방식의 CBC 모드는 출력 암호문이 다음 평문 블록에 영향을 미치게 하여 각 암호문 블록이 전단의 암호문의 영향을 받도록 만든 방식으로 ECB에서 발생하는 동일한 평문에 의한 동일한 암호문이 발생하지 않도록 구성한 동작 모드이다. CBC 모드 동작은 아래 그림과 같이 처음 입력된 평문 블록은 초기 벡터 (initial vector)와EX-OR되어 DES 암호기에 입력된다. 암호기 출력 암호문 은 다음 단 평문 블록 와 EX-OR되어 DES 암호기에 입력된다.


    CBC 모드 동작 중 발생하는 비트 손실이나 오류에 대한 영향을 검토해보자. 전송 중에 암호문 블럭 c에서 발생하는 한 비트의 오류는 복호화된 해당 평문 블럭 m에서는 여러 비트의 영향을 주게되며 다음 단의 복호화된 평문 블럭 mi +1 에는 한 비트의 오류를 유발하게 된다.

    물론 그 다음 단의 복호화된 평문 블럭 mi+2 에는 영향을 주지 않는다. CBC 방식의 특징은 현 단계에서 생성되는 암호문이 그 다음으로 생성되는 암호문 블럭에 영향을 미치기 때문에 특정 암호문 블럭이 전달되는 과정에서 발생되는 채널상의 잡음에 의한 오류는 해당 암호문 뿐만아니라 그 다음 암호문에도 그 효과가 연장된다.

    한편 CBC 모드의 평문 블럭 m에서의 오류 발생시 암호문 분석에 미치는 영향을 살펴보자.

    평문 블럭 m에서의 한 비트 오류는 그 다음에 출력되는 모든 암호문 블럭 ci  ci+1 ci+2에 영향을 미치게 된다. 이러한 특징은 메시지 인증에 유용하게 사용될 수 있다. 구체적으로 말하면 이러한 특징은 문서 인증 부호 MAC(message authentication code)에 사용될  수 있다.


       3) CFB(Cipher FeedBack)

    CFB 모드는 블록 암호가 스트림 암호처럼 동작하도록 한다. CBC처럼, 이 모드도 초기벡터를 이용하지만, 내부 처리가 조금 더 필요하다. CFB 모드에서 블록 암호는 블록 크기보다 작은 데이터의 조각을 암호화할 수 있다. 실제로, CFB는 하나의 비트에서부터 블록까지 어떠한 크기의 데이터도 암호화할 수 있다. 일반적으로, CFB는 동시에 한 바이트(8비트)를 암호화하거나 복호화하는 데 사용하며 이것을 CFB8이라고 한다. 

    아래 그림은 CFB8을 이용한 한 바이트의 암호화를 보여준다.


    『 암호화 (p→c) 
        ① 블록 암호를 이용하여 기본적 암호의 블록 크기만큼 버퍼를 암호화.
             초기에 버퍼는 IV로 채워진다.

         ② 암호화된 버퍼의 최좌단 비트 수와 평문은 XOR로 연산됨. 그 결과 암호문이 출력됨.
             암호화된 버퍼의 나머지는 폐기됨.
             CFB8에서, 암호화된 버퍼의 최좌단 바이트는 평문과 XOR 

         ③ CFB8에서, 버퍼는 한 바이트 왼쪽으로 이동.
              암호문은 버퍼의 오른쪽편에 있는 빈 공간을 채우는 데 사용된다.
              이 버퍼는 다음 암호화에서 다시 사용될 것이다.
              이 과정이 계속되면, 버퍼는 전부 암호문으로 채우는 데 사용될 것이다.

    『 복호화 (c→p) 
       ① 버퍼는 블록 암호를 이용하여 암호화한다. 
            암호문 바이트를 복호화할 지라도, 여전히 버퍼를 암호화 하기 위해 블록 암호를 이용
            한다.

        ② 암호화된 버퍼의 최좌단 비트는 평문 출력을 처리하는 암호문과 XOR로 연산된다.
             다시, 암호화된 버퍼의 나머지는 폐기된다.

        ③ 원래 버퍼는 왼쪽으로 이동하고, 암호문으로 채워진다. 
            이 버퍼는 다음 복호화에서 다시 사용될 것이다.


       4) OFB(Output FeedBack)

    OFB 모드는 내부 버퍼가 갱신되는 것을 제외하고 CFB모드처럼 동작한다.

    내부 버퍼가 왼쪽으로 이동될 때, 오른쪽의 공간은 암호화된 버퍼의 가장 왼쪽 비트로 채워

    진다.
     이론적으로, OFB는 암호의 블록 크기보다 작거나 같은 어떠한 비트 크기로도 사용될 

    수 있다. 그러나 피드백 크기가 기본적 암호의 블록 크기보다 작을 때 OFB의 성능은 떨어진

    다. 단지 피드백 크기가 암호 블록 크기와 같을 때에만 사용하는 것이 좋다.



    <DES.h 파일>
    [CODE]
    /************************************************************************/
    /* DES Algorithm!
    /* Jinsik, Park
    /************************************************************************/
    #ifndef DECLARE_DES
    #define DECLARE_DES

    #include "stdafx.h"

    #include <string>
    using namespace std;

    #ifndef DLLEXPORT
    #define DLLEXPORT __declspec( dllexport )
    #endif

    #ifndef DLLIMPORT
    #define DLLIMPORT __declspec( dllimport )
    #endif

    #ifdef DESIMPORT
    class DLLIMPORT CDES
    #else
    class DLLEXPORT CDES
    #endif
    {
    public:
     CDES()
     {}
     //암호하고, 파일 이름을 넘겨 받는다.
     int DESEncrypt( char * pFileName, char * pDesFileName, char * pPassword,  int nLength );

     
     //암호하고, 저장할 파일 이름을 넘겨 받는다.
     int DESDecrypt( char * pFileName, char * pDesFileName, char * pPassword  , int nLength  );

    protected :
     void DESKeyGenerate( char * pKeySource );
     //8 byte가 넘는 key를 8 byte로 줄이기
     void DESKeyTruncate( char * pKeySource );
     //Char를 2진수 코드로 만들기 
     void DESChar2Binary( int * piTarget, char * pSource, int iSourceLen );
     //8개의 2진수를 하나의 Char로 만들기
     char DESBinary2Char( int * iSource );
     void DESSwap32Bit( int * piLeftBit, int * piRightBit, int * piBufferBit );
     void DESRound( int * piLeftData, int * piRightData );
     void DESStart( int iJobType, int iDataSize, char * pSrcData, char * ptgtData );
     int DESOperation(int iJogType, char * pInput, char * pOutput, char * pKey, int nLength);
    };

    #endif
     [/CODE]

    <DES.cpp파일>
    [CODE]
    #include "Stdafx.h"
    #include "Define.h"   //define 
    #include "DES.h"
    /************************************************************************/
    /* DESStart( int iJobType, int iDataSize, char * pSrcData, char * ptgtData )
    /************************************************************************/
    void CDES::DESStart( int iJobType, int iDataSize, char * pSrcData, char * ptgtData )
    {
     int srcBinary[64] = { 0, }, tgtBinary[64] = { 0, }, binaryBuff[64] = { 0, };
     int leftData[32] = { 0, }, rightData[32] = { 0, }, swapBuff[32] = { 0, };
     int tgtIndex=0;
     char textBuff[9] = { '\0', };
     int i, j, k;

     // Data의 끝까지 가면서 암호화, 복호화 한다 
     for( i = 0, tgtIndex = 0; i < iDataSize; i++ ) 
     { 
      memset( textBuff, 0, 9 );
      // srcData에서 8자씩 textBuff로복사 
      memcpy( textBuff, ( pSrcData + tgtIndex ), 8 ); 

      // textBuff를 binary로  
      DESChar2Binary( binaryBuff, textBuff, 8 ); 

      // Initial Permutation 
      for( j = 0; j < 64; j++ )  
       srcBinary[j] = binaryBuff[ MAT_IP[j]-1 ];

      //Encryption일 경우
      if( iJobType == ENCRYPT ) 
      {
       // 하나의 64bit를 둘의 left, right data 로 나눈다
       for(j=0; j<32; j++)
       { 
        leftData[j] = srcBinary[j];
        rightData[j] = srcBinary[j+32];
       }
      }
      // 복호화 할경우 left data와 right data의 위치를 바꾼다 
      else  
      {
       // 하나의 64bit를 둘의 left, right data 로 나눈다
       for( j = 0; j < 32; j++ )
       { 
        rightData[j] = srcBinary[j];
        leftData[j] = srcBinary[j+32];
       }
      }

      DESRound( leftData, rightData );

      //Encrypt일 경우
      if( iJobType == ENCRYPT ) 
       DESSwap32Bit( leftData, rightData, swapBuff );

      // 둘의 left, right data 를 하나의 64bit로 합한다 
      for( j = 0; j < 32; j++ )
      { 
       // 또한 32bit swap을 한다 
       binaryBuff[j] = rightData[j]; 
       binaryBuff[j+32] = leftData[j];
      }

      // Inverse Initial Permutation 
      for( j = 0; j < 64; j++ ) 
       tgtBinary[j] = binaryBuff[ MAT_IP_1[j]-1 ];

      memset( textBuff, 0, 9 );

      // tgtBinary를 ascii로 변형후 textBuff에 저장 
      for( j = 0, k = 0; j < 64; j += 8, k++ ) 
       textBuff[k] = DESBinary2Char( & tgtBinary[j] );


      // textBuff의 내용을 tgtData로 복사 
      memcpy( ( ptgtData + tgtIndex ), textBuff, 8 ); 
      tgtIndex += 8;
     }
    }

    /************************************************************************/
    /* DESSwap32Bit( int * piLeftBit, int * piRightBit, int * piBufferBit )
    /************************************************************************/
    void CDES::DESSwap32Bit( int * piLeftBit, int * piRightBit, int * piBufferBit )
    {
     int i;

     for( i = 0; i < 32; i++)
      piBufferBit[i] = piLeftBit[i];

     for( i = 0; i < 32; i++ )
      piLeftBit[i] = piRightBit[i];

     for( i = 0; i < 32; i++ )
      piRightBit[i] = piBufferBit[i];
    }

    /************************************************************************/
    /* DESRound( int * piLeftData, int * piRightData )
    /************************************************************************/
    void CDES::DESRound( int * piLeftData, int * piRightData )
    {
     int sboxOut[ 32 ] = { 0, }, leftFinal[ 32 ] = { 0, }, permuOut[ 32 ] = { 0, };
     int eBuff[ 48 ] = { 0, }, eBuffOut[ 48 ] = { 0, };
     int sboxRow, sboxCol, sboxValue;
     int i, j, k, keyIndex;

     //16번의 round를 거친다 
     for( keyIndex = 0; keyIndex < 16; keyIndex++ )
     { 
      for( i = 0; i < 32; i++ )
       leftFinal[ i ] = * ( piRightData + i );

      /* E table 적용 */
      for( i = 0; i < 48; i++ )
      {  
       eBuff[ i ] = * ( piRightData + ( MAT_EXP[ i ] -1 ) );
      }

      // eBuff xor key 
      for( i = 0; i < 48; i++ )
      {  
       eBuffOut[i] = eBuff[i] ^ KEY[keyIndex][i];
      }

      // sBox 적용 
      for( i = 0, j = 0, k = 0; i < 8; i++ )
      { 
       sboxRow = (eBuffOut[j+0] * 2) + eBuffOut[j+5];
       sboxCol = (eBuffOut[j+1] * 8) + (eBuffOut[j+2] * 4) + (eBuffOut[j+3] * 2) + eBuffOut[j+4];
       sboxValue = MAT_SBOX[i][sboxRow][sboxCol];
       sboxOut[k]   = ((sboxValue & 8)  ? 1:0 );
       sboxOut[k+1] = ((sboxValue & 4)  ? 1:0 );
       sboxOut[k+2] = ((sboxValue & 2)  ? 1:0 );
       sboxOut[k+3] = sboxValue & 1; 
       j += 6;
       k += 4;
      }

      //sboxOut 에 p table적용 
      for( i = 0; i < 32; i++ )  
       permuOut[ i ] = sboxOut[ MAT_PERMU[ i ] - 1 ];  

      // permuOut xor leftData 를 rightData에 저장
      for( i = 0; i < 32; i++ ) 
      {  
       * ( piRightData + i ) = * ( piLeftData + i ) ^ permuOut[ i ];
      }

      //첨에 복사한 leftFinal을 leftData에 복사 
      for( i = 0; i < 32; i++ )  
       * ( piLeftData + i ) = leftFinal[ i ];
     }
    }

    /************************************************************************/
    /* DESKeyGenerate( char * pKeySource )
    /************************************************************************/
    void CDES::DESKeyGenerate( char * pKeySource )
    {
     int keySchedule[ 16 ] = { 1,1,2,2,2,2,2,2,1,2,2,2,2,2,2,1 };
     int binaryKey[ 64 ] = { 0, };
     int pc1[ 56 ] = { 0, };
     char tempKey[ 9];
     int i, j, k, tmp1, tmp2, srcLen;

     //입력된 암호의 길이가 8자 이상일 경우 Truncate해 줌
     if( ( strlen( pKeySource ) ) > 8 ) /* 입력된 암호의 길이가 8자 이상이면 */
      DESKeyTruncate( pKeySource );

     strncpy( tempKey, pKeySource, 8 );
     tempKey[8] = '\0';

     srcLen = strlen( tempKey );
     DESChar2Binary( binaryKey, tempKey, srcLen );
     memset( tempKey, 0, 8 );

     //64bit를 56bit로 줄이는거 
     for( i = 0; i < 56; i++ )  
      pc1[i] = binaryKey[ MAT_PC_1[i]-1 ];

     for( i = 0; i < 16; i++ )
     {
      for( j = 0; j < keySchedule[ i ]; j++ )
      {
       tmp1 = pc1[0];
       tmp2 = pc1[28];
       for(k=1; k<28; k++)
       {
        pc1[k-1] = pc1[k];
        pc1[k+27] = pc1[k+28];
       }
       pc1[27] = tmp1;
       pc1[55] = tmp2;
      }
      //56bit 를 48bit로 줄여서 저장 - 최종 key 
      for(j=0; j<48; j++)  
       KEY[i][j] = pc1[ MAT_PC_2[j]-1 ];
     }
    }

    /************************************************************************/
    /* DESKeyTruncate( char * pKeySource )
    /************************************************************************/
    void CDES::DESKeyTruncate( char * pKeySource )
    {
     char * temp;
     int srcLen = 0, dev4;
     int i, j;

     srcLen = strlen( pKeySource );
     temp = ( char * )malloc( srcLen + 1 );
     memset( temp, 0, srcLen +1 );
     strcpy( temp, pKeySource );
     memset( pKeySource, 0, srcLen );

     dev4 = srcLen / 4;

     srcLen-- ;

     for(i=0,j=0;i<4;i++,j+=dev4)
     {
      * ( pKeySource + i ) = * ( temp + srcLen - j );
      * ( pKeySource + i + 4 ) = * ( temp + j );
     }  
    }

    /************************************************************************/
    /* DESChar2Binary( int * piTarget, char * pSource, int iSourceLen )
    /* char 를 binary형태로 만들기 
    /************************************************************************/
    void CDES::DESChar2Binary( int * piTarget, char * pSource, int iSourceLen )
    {
     int targetIndex = 0, i, j;
     char oneChar, temp1, temp2;

     for( i = 0 ; i < iSourceLen; i++)
     {
      oneChar=*( pSource + i);
      for( j = 7; j >= 0; j-- )
      {
       temp1 = oneChar >> j;
       temp2 = temp1 & 0x0001;
       * ( piTarget + targetIndex ) = ( temp2 == 1 ? 1 : 0 );
       targetIndex++;
      }
     }
    }

    /************************************************************************/
    /* DESBinary2Char( int * iSource )
    /* 8개의 2진수를 하나의 Char로 만듬 
    /************************************************************************/
    char CDES::DESBinary2Char( int * iSource )
    {
     char temp;
     int i, j, inTemp=1, chTemp=0;

     for( i = 0; i < 8; i++ )
     {
      inTemp=1;
      if( * ( iSource + i) ) 
      {
       for(j=7; j>i; j--)
        inTemp *= 2;
       chTemp += inTemp;
      }
     }
     temp=chTemp;
     return temp;
    }

    /************************************************************************/
    /* DESEncrypt( char * pFileName, char * pDesFileName, char * pPassword  )
    /************************************************************************/
    int CDES::DESEncrypt( char * pFileName, char * pDesFileName, char * pPassword, int nLength  )
    {
     return DESOperation( ENCRYPT, pFileName, pDesFileName, pPassword, nLength);
    }

    /************************************************************************/
    /* DESDecrypt( char * pFileName, char * pDesFileName, char * pPassword  )
    /************************************************************************/
    int CDES::DESDecrypt( char * pFileName, char * pDesFileName, char * pPassword, int nLength  )
    {
     return DESOperation( DECRYPT, pFileName, pDesFileName, pPassword, nLength);
    }

    /************************************************************************/
    /* DESOperation(int iJogType, char * pFileName, char * pDesFileName, char * pPassword  )
    /************************************************************************/
    int CDES::DESOperation(int iJogType, char * pInput, char * pOutput, char * pKey, int nLength)
    {
     int tempKey[16][48];
     long int datasize = 0, readSize = 0, writesize = nLength;
     char * pInputBuff, * pOutputBuff;
     char nameTemp[100] = {0, };
     char qprkDes[100] = {0, };
     int i, j;

     DESKeyGenerate(pKey);

     
     if(iJogType == DECRYPT)
     {
      for(i=0; i<16; i++)
      {
       for(j=0; j<48; j++)
       {
        tempKey[15-i][j] = KEY[i][j];
       }
      }
      for(i=0; i<16; i++)
      {
       for(j=0; j<48; j++)
       {
        KEY[i][j] = tempKey[i][j];
       }
      }
     }
     
     pInputBuff = new char [BUFFSIZE];
     pOutputBuff = new char [BUFFSIZE];

     int iCount = 0;

     int iRength = ( ! iJogType ) ? (( nLength / RWSIZE ) + 1 ) :
             ( nLength / RWSIZE );
     for( i = 0; i < iRength ; i++ )
     { 
      ZeroMemory(pInputBuff, BUFFSIZE);
      ZeroMemory(pOutputBuff, BUFFSIZE);

      if(writesize > RWSIZE)
      {
       memcpy(pInputBuff, &pInput[readSize], RWSIZE);
      }
      else
      {
       memcpy(pInputBuff, &pInput[readSize], writesize);
      }
     
      //Encrypt 시작
      DESStart( iJogType, RWSIZE / 8, pInputBuff, pOutputBuff ); 
      //memcpy(&pOutput[readSize], pOutputBuff, RWSIZE);
     
      if(writesize > RWSIZE)
       memcpy(&pOutput[readSize], pOutputBuff, RWSIZE);
      
      readSize += RWSIZE;
      writesize -=  RWSIZE;
     }

     delete [] pInputBuff;
     pInputBuff = NULL;
     delete [] pOutputBuff;
     pOutputBuff = NULL;
     
     return readSize - writesize; 
    }
    [/CODE]

 

include <> 와 "" 의 차이

Programming/C++ | 2011. 11. 29. 03:32
Posted by 신이내린프로그래머
인터넷을 돌아다니가 볼랜드포럼의 wfc.h라는 것을 받았다. 
Windows Foundation Classes라고 한다.
이것을 C++빌더에서 사용해보자.

인클루드 패스에 대해서 살펴보자.

인클루드 파일을 지정하는 데는 두가지 방법이 있는데,
첫번째는 인클루드 
디렉토리로서 디폴트로 지정된 디렉토리에 있는 경우고,
두번째는 그외의, 그러니까 

소스가 있는 현재 디렉토리에 있든지 다른 디렉토리에 있을 경우이다. 

#include <stdio.h> 라고 하면, 이 인클루드 파일은 현재 컴파일하고 있는 
컴파일러에 디폴트로 세팅된 디렉토리에서 검색하게 된다. 빌더의 경우에는 
프로젝트 옵션에서 이 디폴트 인클루드 디렉토리를 설정하게 되어있는데, 
빌더 설치 직후에는 다음과 같이 설정되어 있다. 
$(BCB)\include;$(BCB)\include\vcl 
이 말은, 빌더의 루트 밑의 /include 디렉토리와 빌더 루트 밑의 /include/vcl 
디렉토리를 디폴트 인클루드 디렉토리로 설정했다는 뜻이 된다. 

한편, 디폴트 인클루드 디렉토리로 설정되지 않은 파일을 인클루드 할 때는, 
인클루드 프리프로세서에서 < 와 > 대신 "으로 앞뒤를 묶는다. 
#include "myheader.h" 
패스를 별도로 지정하지 않았으므로 현재 프로젝트 소스가 있는 디렉토리를 
대상으로 찾아보게 된다. 만약 다른 디렉토리라면, 
#include "..\myheader.h" 
라든지, 
#include "c:\MyHaeders\myheader.h" 
이런식으로 지정할 수 있다. 

소스 디렉토리에다 넣었다면 
디폴트 인클루드 디렉토리에 소스 디렉토리를 추가하든지, 혹은 인클루드 
프리프로세서 자체에서 소스 디렉토리의 패스를 지정해주면 된다. 
그러니까... 
#include "c:\Program Files\Borland\Cbuilder4\Source\wfc.h" 
라고 하면 됨. 
 

JPEG에 사용된 압축 알고리즘

Programming/Image Processing | 2011. 11. 27. 01:38
Posted by 신이내린프로그래머

JPEG 국제 표준으로 정식 명칭은 ‘Digital Compression and Coding of Continuous Tone Still Image’이다이는 ISO/IEC 10918-1(ITU권고 T.81) 표준화되어 있다.

JPEG에서는 DC성분과 AC성분을 따로 압축한다.  DC성분은 DCT 행한 첫째 값이고 AC성분은 나머지 값을 말한다.

JPEG 데이터를 8x8 블록 단위로 읽어서 처리한다

DC성분은 12가지의 값을 가질 있다그리고 화면에서 DC값들은 비슷한 경우가 많기 때문에 새로운 블록을 처리할 이전 블록의 DC값과 비교해서 차이를 이용해 압축을 수행한다.

AC성분은지그재그 스캔 데이터를 가지고 압축한다기본적인 JPEG에서는 AC값이 모두 10가지 값을 가질 있다.  AC값은 바이트를 4비트씩 나눠 앞쪽에는 연속된 데이터의 개수가 들어가고 뒤에는 코드값이 들어간다( Run Length Coding). 또한 0 16 이상 연속으로 나오면 이를 블록의 (EOB)이라고 인식한다.

이와 같은 식으로 우선 한번의 작업을 거친 후에 데이터를 가지고 엔트로피 코딩에 들어간다.

 

허프만 코딩(Huffman coding)

JPEG에서 사용하는 엔트로피 코딩은 Huffman coding Arithmetic coding 가지이다 가지를 모두 사용하는 것은 아니고 파일에 따라서 어떤 파일은 Huffman coding, 어떤 파일은 Arithmetic coding 사용하는 것이다.  Huffman coding 허프만이란 사람이 개발한 코드로 구현하기 쉽고 사용료가 없기 때문에 가장 많이 사용되는 코드이다. Arithmetic coding IBM에서 개발한 코드로, 압축률은 Huffman coding보다 약간 좋다고 알려져 있지만, 실수연산을 하기 때문에 코드가 복잡하고 IBM 사용료를 지불해야 하기 때문에 사용하지 않는다.

 

파라미터

크기(bits)

   

DHT

16

FFC4(h)

 허프만 테이블임을 나타내는 마커 코드

Lh

16

 

 허프만 테이블의 길이

Tc

4

0,1

 table class(0=DC, 1=AC)

Th

4

0,1

 Identifier

Li

8

0-255

 코드길이가 i 허프만 코드의 개수

Vi,j

8

0-255

 허프만 코드에 해당하는

1 허프만 테이블 구문









Discrete Cosine Transform(DCT)

1차원 DCT           : C(u) = alpha (u)  sum from {x=0} to {N-1} f(x) cos [ { (2x+1) u pi } over { 2 N } ]

1차원 IDCT          :f(x) = sum from {u=0} to {N-1} alpha (u) C(u) cos[ { (2x+1) u pi } over { 2 N } ]

u, x 0에서 N-1까지이고, alpha 다음과 같이 정의된다.

alpha (u) &= root { 1 over N } ~~~~~for~ u=0# &=root { 2 over N } ~~~~~for~u!=1

2차원 DCT              :C(u,v) = alpha (u) alpha (v) sum from { x=0 } to { n-1 } sum from {y=0} to {n-1} f(u,v) cos [ {2x+1) u pi } over { 2N } ] cos [ { (2y+1) u pi } over {2N} ]

2차원 IDCT          :f(u,v) = sum from { x=0 } to { n-1 } sum from {y=0} to {n-1} alpha (u) alpha(v) C(u, v) cos [ {2x+1) u pi } over { 2N } ] cos [ { (2y+1) u pi } over {2N} ]

 

JPEG Encoding, Decoding 방법

입력 영상을 8x8블럭으로 나눈다.

각각의 블록에 대해 DCT 행한다.

양자화를 행한다.

계수값을 코딩한다.

헤더를 붙이고 형식에 맞춰서 저장한다.

파일을 읽는다.

값을 디코딩한다.

역양자화를 행한다.

각각의 블록에 대하여 IDCT 행한다.

원영상을 재구성한다.





JPEG 파일에서 Huffman 코딩 : 
JPEG 파일에서는 DCT-양자화를 거친 데이터를 Huffman 코딩 방식으로 압축을 한다. Huffman 코드의 최대 길이는 16비트로 제한되어 있다. DCT계수 가운데 DC 계수와 AC 계수를 따로 분리하여 코딩하고 필요에 따라 YCbCr 성분 별로도 따로 코딩을 한다. Huffman 코딩은 서로 같은 데이터가 많을 수록 압축 효율이 좋기 때문에 이렇게 서로 비슷한 성분들끼리 분리하여 코딩을 하는 것이다.
보통 DC계수의 크기는 AC계수의 크기보다 크며 양자화된 AC계수는 대부분이 0이다. AC계수끼리 묶으면 대부분의 데이터가 0인데 보다 압축효율을 높이기 위해 0으로 된 데이터들은 PCX파일에서 사용한 Run-length encoding 방식으로 압축을 한다. 즉, 데이터 자체를 저장하는 것이 아니라 0의 개수를 저장하는 것이다. 0이 아닌 AC계수와 DC계수는 Huffman 인코딩을 하는데 값 자체를 인코딩하는 것이 아니고 인접한 값과의 차이를 인코딩한다. 예를 들어 DC 계수 배열이 122, 123, 124, 125, 126 이런 식이라면 실제 값이 아닌 인접한 값과의 차이인 122, 1, 1, 1, 1을 인코딩하는 것이다. 이렇게 하면 서로 똑같은 값들이 많아지기 때문에 값 자체를 인코딩할 때 보다 훨씬 더 압축 효율이 높아진다. 이런 이유 때문에 인접한 픽셀들끼리의 변화가 적은 사진 이미지가  도형 이미지보다 압축 효율이 좋다.

'Programming > Image Processing' 카테고리의 다른 글

영상처리  (0) 2011.11.30
레이저마우스  (0) 2011.11.26
 

블로그 이미지

신이내린프로그래머

카테고리

Category (22)
Programming (19)
... (1)