👆이전 포스팅 글에서는 SHA-256의 함수 중 SHA256_Encrpyt()와 SHA256_Init()을 분석하였다.
이어서 SHA256_Process()와 SHA256_Transform()을 분석하겠다.
1. 소스파일
1.3. SHA256_Process()
void SHA256_Process( OUT SHA256_INFO *Info, IN const BYTE *pszMessage, IN UINT uDataLen )
{
if ((Info->uLowLength += (uDataLen << 3)) < 0)
Info->uHighLength++;
Info->uHighLength += (uDataLen >> 29);
while (uDataLen >= SHA256_DIGEST_BLOCKLEN)
{
memcpy((UCHAR_PTR)Info->szBuffer, pszMessage, (SINT)SHA256_DIGEST_BLOCKLEN);
SHA256_Transform((ULONG_PTR)Info->szBuffer, Info->uChainVar);
pszMessage += SHA256_DIGEST_BLOCKLEN;
uDataLen -= SHA256_DIGEST_BLOCKLEN;
}
memcpy((UCHAR_PTR)Info->szBuffer, pszMessage, uDataLen);
}
uLowLength와 uHighLength는 메시지 크기를 표현하는 변수다.
uLowLength에 메시지 크기를 담는다.
uLowLength에 담을 수 없을 만큼 메시지 크기가 클 때 uHighLength를 쓴다.
while문은 평문 메시지를 512bit 크기씩 나눠 블록을 만들고, 해시값을 생성하는 작업을 수행한다.
#define SHA256_DIGEST_BLOCKLEN 64
64 x 8 = 512
512bit만큼 평문을 버퍼에 저장하여 SHA256_Transform()을 실행한다.
SHA256_Transform() 실행을 마치면...
평문(pszMessage)에 SHA256_DIGEST_BLOCKLEN을 더하고, 평문 크기(uDataLen)에는 SHA256_DIGEST_BLOCKLEN을 뺀다.
SHA256_DIGEST_BLOCKLEN만큼을 더하고 빼는 이유?
간단히 말하면 while문을 통해 512bit 블록씩 계산하기 위해서.
☝첫 번째 while문을 실행할 때를 생각해보자
배열로 이루어진 평문(pszMessage)에서 0~63번째 배열은 SHA256_Transform() 함수를 통해 해시값 생성이 완료된다.
그러면 이제 그다음 배열을 위한 수행을 해야 한다.
그다음 두 번째 반복문(또는 실행문)에서 64번부터의 배열에 대한 해시값을 생성하기 위해서 SHA256_DIGEST_BLOCKLEN을 더한다. (pszMessage는 포인터 배열로 되어있어서 덧셈을 통해 배열 설정)
uDataLen은 while문의 조건문인 uDataLen >= SHA256_DIGEST_BLOCKLEN
에서 빠져나오기 위해 uDataLen 또한 SHA256_DIGEST_BLOCKLEN을 뺀다. (512bit 크기의 블록을 만들기 때문에)
while문 실행이 종료되면
512bit 블록씩 해시값을 생성하고 남은 메시지는 버퍼에 저장한다.
이에 대한 해시값은 SHA256_Close() 함수에서 처리한다.
1.4. SHA256_Close()
void SHA256_Close( OUT SHA256_INFO *Info, IN BYTE *pszDigest )
{
ULONG i, Index;
Index = (Info->uLowLength >> 3) % SHA256_DIGEST_BLOCKLEN;
Info->szBuffer[Index++] = 0x80;
if (Index > SHA256_DIGEST_BLOCKLEN - 8)
{
memset((UCHAR_PTR)Info->szBuffer + Index, 0, (SINT)(SHA256_DIGEST_BLOCKLEN - Index));
SHA256_Transform((ULONG_PTR)Info->szBuffer, Info->uChainVar);
memset((UCHAR_PTR)Info->szBuffer, 0, (SINT)SHA256_DIGEST_BLOCKLEN - 8);
}
else
memset((UCHAR_PTR)Info->szBuffer + Index, 0, (SINT)(SHA256_DIGEST_BLOCKLEN - Index - 8));
#if defined(LITTLE_ENDIAN)
Info->uLowLength = ENDIAN_REVERSE_ULONG(Info->uLowLength);
Info->uHighLength = ENDIAN_REVERSE_ULONG(Info->uHighLength);
#endif
((ULONG_PTR)Info->szBuffer)[SHA256_DIGEST_BLOCKLEN / 4 - 2] = Info->uHighLength;
((ULONG_PTR)Info->szBuffer)[SHA256_DIGEST_BLOCKLEN / 4 - 1] = Info->uLowLength;
SHA256_Transform((ULONG_PTR)Info->szBuffer, Info->uChainVar);
for (i = 0; i < SHA256_DIGEST_VALUELEN; i += 4)
BIG_D2B((Info->uChainVar)[i / 4], &(pszDigest[i]));
}
SHA256_Process() 함수에서 512bit 블록씩 메시지를 나누어 해시값을 생성하고 남은 메시지에 대한 해시값을 생성한다.
if문에서는 메시지 크기에 대한 값을 넣을 부분(size: 8)을 제외하고 메시지 뒤에 0을 추가한다. (Padding)
Index = (Info->uLowLength >> 3) % SHA256_DIGEST_BLOCKLEN;
Info->szBuffer[Index++] = 0x80;
Index는 메시지의 마지막이 어디인지 표시하기 위한 mark 역할을 한다.
if문의 조건이 만족하면, 메시지의 값이 512bit 블록 안에 다 담기지 못할 경우에 대한 동작을 한다.
memset((UCHAR_PTR)Info->szBuffer + Index, 0, (SINT)(SHA256_DIGEST_BLOCKLEN - Index - 8));
if문의 조건에 만족하지 못할 경우에는 위의 코드를 실행한다.
메시지와 mark이후의 부분을 해당 사이즈만큼 0으로 초기화한다.
리틀 엔디안(Little Endian)일 경우
※리틀 엔디안: 낮은 주소에 낮은 바이트(LSB, Least Significant Bit)
시스템이 리틀 엔디안으로 설정되어 있다면, (헤더 파일에서 리틀 엔디안으로 설정한 것을 알 수 있음)
매크로 함수를 이용하여 uLowLenght와 uHighLength의 값을 빅엔디안 형식으로 변환해준다.
szBuffer의 변수형을 바꾼 이유?
((ULONG_PTR)Info->szBuffer)[SHA256_DIGEST_BLOCKLEN / 4 - 2] = Info->uHighLength;
((ULONG_PTR)Info->szBuffer)[SHA256_DIGEST_BLOCKLEN / 4 - 1] = Info->uLowLength;
⬇
szBuffer[14] = uHighLength;
szBuffer[15] = uLowLength;
szBuffer에 메시지 크기에 대한 데이터를 넣기 위해서.
szBuffer는 원래 BYTE(unsigned char)로, 배열 하나당 1byte(8bit)이다.
uHighLength와 uLowLength는 UINT(unsigned int)로, 4byte(32bit)이다.
4byte 크기의 uHighLength와 uLowLength를 szBuffer에 담기 위해서는 szBuffer의 크기를 늘여야 한다.
그래서 크기가 4byte인 ULONG(unsigned long)를 szBuffer에 사용한다.
szBuffer[14:15]인 이유?
16 x 32 = 512
배열 갯수 x 크기(ULONG)
512bit에서 마지막 64bit는 uHighLength와 uLowLength의 자리가 된다.
(배열 하나의 크기가 32bit)
마지막 for문에서는 BIG_D2B(D, B) 매크로 함수를 이용하여 256bit의 해시값을 생성한다.
(pszDigest는 unsigned char(1byte), Info->uChainVar는 unsigned int(4byte))
#define SHA256_DIGEST_VALUELEN 32
32bit x 8 = 256bit
#if defined(BIG_ENDIAN)
#define BIG_B2D(B, D) D = *(ULONG_PTR)(B)
#define BIG_D2B(D, B) *(ULONG_PTR)(B) = (ULONG)(D)
#define LITTLE_B2D(B, D) D = ENDIAN_REVERSE_ULONG(*(ULONG_PTR)(B))
#define LITTLE_D2B(D, B) *(ULONG_PTR)(B) = ENDIAN_REVERSE_ULONG(D)
#elif defined(LITTLE_ENDIAN)
#define BIG_B2D(B, D) D = ENDIAN_REVERSE_ULONG(*(ULONG_PTR)(B))
#define BIG_D2B(D, B) *(ULONG_PTR)(B) = ENDIAN_REVERSE_ULONG(D)
#define LITTLE_B2D(B, D) D = *(ULONG_PTR)(B)
#define LITTLE_D2B(D, B) *(ULONG_PTR)(B) = (ULONG)(D)
#else
#error ERROR : Invalid DataChangeType
#endif
- 출처 -
'Security > SHA' 카테고리의 다른 글
[SHA-256] 코드 및 알고리즘 분석 - (5) (0) | 2021.07.16 |
---|---|
[SHA-256] 코드 및 알고리즘 분석 - (4) (0) | 2021.07.14 |
[SHA-256] 코드 및 알고리즘 분석 - (2) (0) | 2021.07.13 |
[SHA-256] 코드 및 알고리즘 분석 - (1) (0) | 2021.07.12 |
SHA-256 개념 간단 설명 (0) | 2021.07.12 |