23
두 번째 판은 많이 개정되었고 확장됐다 . 추가적인 암호 알고리즘의 예 , 가령 , Rabin과띠 Gamal 의 처리 과정을 덧붙였다 . 그리고 RSA 처리 과정의 구현에서 해시 함수 RIPEMD-160 PKCS #1 에 따른 형식을 채택했다 . 처리 과정에 허점을 주는 소스 코드의 에러에 대한 검토도 또한 다루었 . 전문을 확장하거나 많은 부분을 좀 더 분명하게 했다 . 게다가 , 독자가 이해하기 쉽도록 하기 위해서 , 몇몇 소스 코드는 책에 나와 있는 것과 CD-ROM에 있는 것이 다르다 . 기술적으로 상세 한 내용을 다루는 소스 코드가 항상 중요한 것은 아니며 , 효율적인 소스 코드가 항상 매력적이고 읽기쉬운 코드가 될수는 없는 것이다 . 그리고 효율성에 대해 말하자면 , 부록 D 에서 FLINT/C 라이브러리의 런타임과 GNU Multiprecision 라이브러리의 런타임을 비교했다 . 비교한 결과 , FLINT/C 의 지수 연산 루틴이 상당히 효율적이라는 것을 알게 되었다 . 그리고 부록 F에는 몇몇 산술 연산과 정수론 패키지에 대한 참고자료를 수록 했다 . FLINT/C에 몇 가지 함수들을 추가했고 그러면서 철저하게 검토했다 . 그런 과정에서 많은 에러들 과 불명확한 점들이 삭제되었다 . 그리고 테스트 함수들을 추가적으로 개발했고 , 기존에 있던 테스 트 함수들을 확장했다 . 보안 모드가 구현되었고 , 그에 따라 보안적으로 중요한 변수들은 함수 안 에서 0으로 덮어써지고 지워지도록 했다 . 모든 C, C++ 함수들은 이제 확실하게 정비되었고 이 책의 부록에 함수들의 목록을 실었다 . 현재의 컴파일러들은 C++ 표준 개발의 변화하는 단계에 있기 때문에 , FLINT/C 패키지의 C++ 모듈들은 xxxxx.h라는 전통적인 C++ 헤더 파일과 ANSI 헤더 파일의새로운것둘다사용될 수 있는 방식으로 설정되었다 . 이와 같은 이유로 , 연산자 new()의 사용에 대해서 항상 널 포인터가 반환되는지를 체크했다 . 이런 형식의 에러처리는 ANSI 표준 예외처리(excep tions)를 사용하지 않 으나 현재의 컴파일러들에서는 이런 것을 사용한다 . 반면에 , 그 방법은 new()throw()를 통해서 에러를 넘기는 표준적인 방식을 따르지만 , 일반적으로 사용하지는 않는다 . 두 번째 판의 서문

C와 C++로 구현하는 암호화 알고리즘 · 2009-12-02 · 독일어문법때문에곤경에빠질때마다, 간단히사이버공간으로 질문을하면곧복호화할수있는알고리즘과함께Michael

  • Upload
    others

  • View
    0

  • Download
    0

Embed Size (px)

Citation preview

Page 1: C와 C++로 구현하는 암호화 알고리즘 · 2009-12-02 · 독일어문법때문에곤경에빠질때마다, 간단히사이버공간으로 질문을하면곧복호화할수있는알고리즘과함께Michael

두 번째 판은 많이 개정되었고 확장됐다. 추가적인 암호 알고리즘의 예, 가령, Rabin과 띠 Gamal

의 처리 과정을 덧붙였다. 그리고 RSA 처리 과정의 구현에서 해시 함수 RIPEMD-160과 PKCS #1

에 따른 형식을 채택했다. 처리 과정에 허점을 주는 소스 코드의 에러에 대한 검토도 또한 다루었

다. 전문을 확장하거나 많은 부분을 좀 더 분명하게 했다. 게다가, 독자가 이해하기 쉽도록 하기

위해서, 몇몇 소스 코드는 책에 나와 있는 것과 CD-ROM에 있는 것이 다르다. 기술적으로 상세

한 내용을 다루는 소스 코드가 항상 중요한 것은 아니며, 효율적인 소스 코드가 항상 매력적이고

읽기 쉬운 코드가 될 수는 없는 것이다.

그리고 효율성에 대해 말하자면, 부록 D에서 FLINT/C 라이브러리의 런타임과 GNU Multiprecision

라이브러리의 런타임을 비교했다. 비교한 결과, FLINT/C의 지수 연산 루틴이 상당히 효율적이라는

것을 알게 되었다. 그리고 부록 F에는 몇몇 산술 연산과 정수론 패키지에 대한 참고자료를 수록

했다.

FLINT/C에 몇 가지 함수들을 추가했고 그러면서 철저하게 검토했다. 그런 과정에서 많은 에러들

과 불명확한 점들이 삭제되었다. 그리고 테스트 함수들을 추가적으로 개발했고, 기존에 있던 테스

트 함수들을 확장했다. 보안 모드가 구현되었고, 그에 따라 보안적으로 중요한 변수들은 함수 안

에서 0으로 덮어써지고 지워지도록 했다. 모든 C, C++ 함수들은 이제 확실하게 정비되었고 이

책의 부록에 함수들의 목록을 실었다.

현재의 컴파일러들은 C++ 표준 개발의 변화하는 단계에 있기 때문에, FLINT/C 패키지의 C++

모듈들은 xxxxx.h라는 전통적인 C++ 헤더 파일과 ANSI 헤더 파일의 새로운 것 둘 다 사용될 수

있는 방식으로 설정되었다. 이와 같은 이유로, 연산자 new()의 사용에 대해서 항상 널 포인터가

반환되는지를 체크했다. 이런 형식의 에러처리는 ANSI 표준 예외처리(exceptions)를 사용하지 않

으나 현재의 컴파일러들에서는 이런 것을 사용한다. 반면에, 그 방법은 new()가 throw()를 통해서

에러를 넘기는 표준적인 방식을 따르지만, 일반적으로 사용하지는 않는다.

두 번째 판의 서문

Page 2: C와 C++로 구현하는 암호화 알고리즘 · 2009-12-02 · 독일어문법때문에곤경에빠질때마다, 간단히사이버공간으로 질문을하면곧복호화할수있는알고리즘과함께Michael

두 번째 판의서문

이 책의 초점은 비대칭 암호를 기반으로 하고 있지만, 미국 국립 표준 연구소(NIST : American

National Institue of Standards and Technology)가 최근에 AES로 공표한 Rijndael에 대한 내용을

마지막 장(19장)에 담았다. 이 책의 주제와는 조금 벗어난 내용을 본 저자를 믿고 넣게 해준, Apress

사의 Gary Cornell에게 감사드린다. 그리고 CD-ROM에 Rijndael을 구현한 소스 코드를 넣게 해준 Vincent

Rijmen, Antoon Bosselaers, Paulo Barreto, 그리고 Brian Gladman에게 감사드린다.

그리고 코멘트를 해주거나 개선사항을 말해주고, 잘못된 부분을 지적해 준, 첫 번째 판의 독자들

에게 감사드린다. 항상 본 저자는 전문 또는 소프트웨어에 아직 남아 있을지 모르는 에러들뿐 아

니라, 앞으로 나올지 모르는 새로운 에러들에 대한 책임이 있다고 생각하고 있다.

다시 한번 아낌없는 헌신과 도움을 주신 Appress사의 Gary Cornell, Hermann Engesser, Dorothea

Glaunsinger, 그리고 Springer-Verlag에 있는 Ulrike Stricker에게 진심 어린 감사를 드린다.

탁월한 전문가적인 안목과 아낌없는 헌신으로 값진 조언를 해준 번역자, David Kramer에게 진심

으로 감사를 드린다. 그가 해준 조언은 이 책의 독일어 판에도 첨가되어 있다.

경고

이 책에 포함되어 있는 프로그램을 사용하기 전에 관련 소프트웨어에 대한 매뉴얼과 기술 문서를

참고하는 것이 좋다. 저자도 출판자도 이 책에 있는 프로그램을 부적절하게 사용해서 생긴 문제,

또는 주의 깊게 검토를 했지만 혹시 남아 있을 프로그램 또는 전문적인 오류 때문에 생긴 문제에

대해서 책임지지 않는다. CD-ROM 안에 있는 프로그램들은 저작권으로 보호되고 출판자의 허락

없이 복사하는 것은 금지된다.

용어 사용

이 책에서 맨 앞자리가 0(leading zeros) 이라는 용어를 자주 사용한다. 이 용어는 공적으로나 사

적으로, 어떤 개인이나 단체, 또는 현재나 과거에 암시적으로 언급되어 만들어진 것이 아니다. 그

러므로 항상 경우에 맞게 일치하는 것은 아니다.

18

Page 3: C와 C++로 구현하는 암호화 알고리즘 · 2009-12-02 · 독일어문법때문에곤경에빠질때마다, 간단히사이버공간으로 질문을하면곧복호화할수있는알고리즘과함께Michael

두 번째 판의 서문

역자 메모

이렇게 멋진 책( bersetzungsvergn gung)을 번역하는데 있어, 저자의 친절하고 유용한 도움으로

더 즐겁게 해나갈 수 있었다. 독일어 문법 때문에 곤경에 빠질 때마다, 간단히 사이버 공간으로

질문을 하면 곧 복호화할 수 있는 알고리즘과 함께 Michael Welschenbach로부터 메시지를 받았

었다.

19

Page 4: C와 C++로 구현하는 암호화 알고리즘 · 2009-12-02 · 독일어문법때문에곤경에빠질때마다, 간단히사이버공간으로 질문을하면곧복호화할수있는알고리즘과함께Michael

정수에 대한 산술 연산을 중점적으로 다루고, 그것을 응용한 컴퓨터 프로그래밍을 다루는 암호책

이 독자는 왜 필요합니까? 일반적으로, 컴퓨터 과학이 필요한 문제들에 비해 이것은 중요한 문제

인가? 프로그래밍 언어가 정해준 표준 데이터 타입만을 가지고 프로그래밍한다면, 산술 연산은 간

단한 일이고, 그것을 구현한 소스 코드는 우리에게 익숙한 +, -, /, * 기호들을 가진 일반적인 모

습을 하고 있을 것이다.

그러나 만약 16 또는 32비트들로 표현하기에는 결과값의 범위가 너무 크다면, 상황은 재미있게

돌아갈 것이다. 기본적인 산술 연산도 더 이상 이런 수에 대해선 사용할 수 없고, 전에는 문제라

고 생각지도 못한 문제를 풀기 위해서 상당한 노력을 투자하는 것 빼고는 할 수 있는게 아무것도

없을 것이다. 정수론에서의 문제들을, 특히 최신의 암호학적인 주제를 연구하는 사람들은 누구나

프로이건, 취미로 하건, 이런 문제에 대해서 익숙할 것이다. 앞으로, 학교에서 배웠던 산술 연산

기법들은 이제 새롭게 손질될 것이고, 때때로 믿을 수 없는 처리 과정을 다루는 독자 자신을 발

견하게 될 것이다.

이런 분야에서 프로그램을 개발하기를 원하는 독자는 이 책에 포함되어 있는 함수들을 찾게 될

것이다. 이 책에 포함되어 있는 함수들은 큰 정수를 계산하기 위해 C, C++로 구현한 함수들이다.

이것이 어떻게 작용할까? 라는 식의 장난으로 만든 것이 아니라, 안정성, 수행능력, 그리고 올바

른 이론적 배경 등 전문적인 요구사항들을 만족하는 함수들과 방법들을 완벽하게 만들었다.

이론과 실제를 접목시키는 것이 이 책의 목표이다. 즉, 이론적인 문헌과 실제 프로그래밍의 문제

사이의 간격을 좁히는 것이다. 이 책의 앞장에서부터 단계를 밟아가며, 큰 정수를 위한 기본적인

계산원리, 유한 환체(ring)와 필드(field)상에서의 산술 연산, 그리고 기초적인 정수론보다 좀 더 복

잡한 함수들을 개발하고, 이러한 원리들이 현대 암호학에 적용될 수 있는 다양하고 많은 가능성들

을 설명할 것이다. 여기에서 만든 프로그램들을 이해하기 위해 필요한 수학적인 원리들이 설명될

것이고, 이런 주제들에 좀 더 깊은 관심을 갖는 독자를 위한 문헌자료에 대한 광범위한 참고자료

첫 번째 판의 서문

Page 5: C와 C++로 구현하는 암호화 알고리즘 · 2009-12-02 · 독일어문법때문에곤경에빠질때마다, 간단히사이버공간으로 질문을하면곧복호화할수있는알고리즘과함께Michael

첫 번째 판의서문

가 구비되어 있을 것이다. 그런 다음에, 우리가 개발한 함수들은 광범위하게 테스트되고, 그 결과

유용하고도 이해하기 쉬운 인터페이스를 갖게 될 것이다.

큰 정수에 대한 표현의 내용부터 시작해서, 다음 장에서 먼저 기본적인 산술 연산을 다룰 것이다.

큰 정수에 대한 덧셈 연산, 뺄셈 연산, 곱셈 연산, 그리고 나눗셈 연산을 위해 강력한 기본 함수

들을 만들 것이다. 이런 함수들을 기초로 해서, 잉여류 클래스에서 모듈러 산술 연산을 설명하고,

관련 함수들을 구현할 것이다. 한 장을 할애해서 모듈러 지수 연산을 집중적으로 다룰 것이다. 그

장에서 모듈러 지수 연산에 대한 수많은 알고리즘을 개발하고 프로그래밍한다.

큰 정수의 입출력, 다양한 베이스로의 변환 등을 개발한 다음에, 기초 산술 연산 함수들을 사용하

는 기본적인 정수론 알고리즘을 공부하고, 그런 다음에 최대공약수와 최소공배수부터 프로그래밍

해 나간다. 그런 다음에, 유한 환체(finite rings)에서 Legendre와 Jacobi 기호, 그리고 역과 제곱근

을 계산하는 문제들로 넘어간다. 그리고, 중국인 나머지 정리(Chinese remainder theorem)와 그것

의 응용을 다룬다.

이런 것과 연결지어 큰 소수를 찾아내는 원리를 자세하게 다루고 강력한 소수 테스트를 프로그래

밍할 것이다.

그 다음 장에서 크기가 큰 랜덤수 생성에 심혈을 기울일 것이다. 암호학적으로 유용한 비트 생성

기를 개발하고 통계적인 특성과 관련해서 테스트할 것이다.

그 다음 장에서 이제까지 만들었던 함수들의 테스트에 신경을 쓸 것이다. 테스트를 위해서 산술

연산의 수학적인 법칙으로부터 특별한 테스트 방법을 이끌어 내고, 효과적인 외부 도구의 구현을

고려할 것이다.

두 번째 파트의 주제는 첫 번째 파트에서 구현한 C 함수들을 객체지향 언어 C++의 구문으로 바

꾸는 과정을 거치며, C++ 클래스 LINT(Large INTegers)를 만드는 것이다. 유연성있는 스트림 함

수들과 조작자(manipulator)들로 LINT 객체의 형식화된 입출력 뿐만 아니라, 예외 상황에서의 에

러처리에 대해 중점을 두고 개발할 것이다. C++로 알고리즘을 만들어서 좋은 점은, 표준 데이터

타입과 LINT 객체의 큰 수들 사이의 경계가 사라진다는 것이다. 그 결과, 구현한 알고리즘의 구

문론적인 닫혀 있음(집합에서의 닫혀 있다는 개념과 비슷함)과 명료함, 간결함을 얻을 수 있다.

22

Page 6: C와 C++로 구현하는 암호화 알고리즘 · 2009-12-02 · 독일어문법때문에곤경에빠질때마다, 간단히사이버공간으로 질문을하면곧복호화할수있는알고리즘과함께Michael

첫 번째 판의 서문

마지막으로, 암복호화와 전자서명을 위한 RSA 시스템을 개발하는 방법들을 설명한다. 그런 과정

에서 비대칭키 암호 알고리즘을 대표하는 RSA의 처리 과정 원리와 그것의 작용을 설명할 것이

고, 이에 대한 예제로 C++의 객체지향 원리에 따라서 최신의 암호학적인 처리 과정의 응용으로

확장이 가능한 핵심 코드를 개발할 것이다.

이제 소프트웨어 라이브러리를 대충 살펴보았다. 끝으로, 소프트웨어의 수행능력을 향상시키기 위

한 곱셈 연산과 나눗셈 연산을 80x86 어셈블러로 구현한 함수들을 제시할 것이다. 부록 D에 어

셈블러가 지원되었을 때와 지원되지 않았을 때의 연산 시간 차이가 표로 만들어져 있다.

이 책을 읽는 모든 독자들이 본 저자와 함께 열의를 가지고 이런 과정을 거쳐가고 - 개인적인 관

심에 따라 특정한 장과 섹션만을 거쳐가도 상관없다 - 각 장과 섹션마다 있는 함수들을 사용해

보길 바란다. 그래서 본 저자는 우리라는 말을 이 책에서 즐겨 사용했다. 이것으로 수학과 컴퓨

터학 사이를 거쳐가는 우리의 여정에서 독자들이 활동적인 역할을 하고, 충분한 이해를 통해 이

책으로부터 최대의 효과를 얻어가기를 바란다. 소프트웨어에 대해서, 독자가 새로운 구현을 통해

함수들의 속도 또는 영역을 확장하는 것이 허용되어 있다.

이 책을 출판할 때 많은 관심과 아낌없는 원조를 해준 Springer-Verlag, 그리고 특히 Hermann Engesser,

Dorothea Glaunsinger, Ulrike Stricker에게 감사를 드린다. J rn Garbers, Josef von Helden, Brigitte Nebelung,

Johannes Ueberberg, 그리고 Helga Welschenbach가 원고를 검토해주었다. 그들의 아낌없는 조언에 대해

서 진심 어린 감사를 드린다. 이런 우리의 노력에도 불구하고 본문과 소프트웨어에 잘못된 점이

남아 있다면, 저자가 책임을 진다. 수 년 동안 원조를 아끼지 않고 수학과 컴퓨터학 사이의 접목

에 대해 통찰력을 보여준 나의 친구들과 동기들, Robert Hammelrath, Franz-Peter Heider, Detlef

Kraus, 그리고 Brigitte Nebelung에게 감사를 드린다.

23

Page 7: C와 C++로 구현하는 암호화 알고리즘 · 2009-12-02 · 독일어문법때문에곤경에빠질때마다, 간단히사이버공간으로 질문을하면곧복호화할수있는알고리즘과함께Michael

IPART

C로 구현하는

산술 연산과 정수론

Cha pte r 1 소개

Cha pte r 2 수체계 : C에서의 큰 정수들에 대한 설명

Cha pte r 3 인터페이스 세만틱스(Inte rface Se ma nt ics )

Cha pte r 4 기본적인 연산 함수들

(Funda me nta l ope rat ion)

Cha pte r 5 모듈러 산술 연산 : 잉여류

(Res idue class )로 계산하기

Cha pte r 6 모듈러 지수 연산

Cha pte r 7 비트 연산과 논리 연산

Cha pte r 8 입력, 출력, 할당, 변환 함수들

Cha pte r 9 동적 레지스터들

Cha pte r 10 기본적인 정수론 산술 연산 함수들

Cha pte r 11 크기가 큰 랜덤수

Cha pte r 12 LINT 클래스를 테스트하는 몇 가지 방법

Page 8: C와 C++로 구현하는 암호화 알고리즘 · 2009-12-02 · 독일어문법때문에곤경에빠질때마다, 간단히사이버공간으로 질문을하면곧복호화할수있는알고리즘과함께Michael

1소개

신은 정수를 만들었다. 그 나머지의 일은 사람의 일이다.

- Leo plod Kro necke r

당신이 제로를 아무 생각없이 본다면 아무것도 없는 것이라고 생각할 것이다.

그러나 당신이 제로를 꿰뚫어 본다면 세상을 이해하게 될 것이다.

- Ro be rt Ka pla n , T he Not hing T hat is : A Nat io nal h isto ry of Ze ro .

Page 9: C와 C++로 구현하는 암호화 알고리즘 · 2009-12-02 · 독일어문법때문에곤경에빠질때마다, 간단히사이버공간으로 질문을하면곧복호화할수있는알고리즘과함께Michael

PART I C로 구현하는산술 연산과정수론

현대의 암호학에 대해 깊이 알기를 원하는 사람은 싫든 좋던 정수론, 수학에서 가장 아름다운 분

야 중의 하나를 파고들어야 한다. 그러나 이 책에서는 암호화 프로그램에 필요없는, 깊숙한 내용

들까지는 다루지 않을 것이다. 우리의 목표는 훨씬 더 가벼운 내용들을 다루는 것이다. 그렇지만,

암호학에 있어서 정수론의 영향은 지대하고, 예부터 유명한 수학자들이 이 분야에 지대한 공헌을

해왔다.

정수론의 근원은 고대(antiquity)에서부터 시작된다. 피타고라스학파 - 그리스의 수학자이며 철학자

인 피타고라스와 그의 유파들은 - B.C. 6세기에 이미 정수 사이의 관계에 대해 깊이 있게 연구하

고 있었고, 중요한 수학적인 업적을 일구어 냈었다. 예를 들면, 초등학교 수학 교과서에도 나오는

피타고라스의 정리이다. 그들은 종교적인 열정으로 모든 수들은 자연수와 상응해야 한다는 견해를

주장했지만, 두 개의 정수로 표현할 수 없는 2 같은 무리수(irrational)의 존재를 발견했을 때 심

각한 딜레마에 빠지게 되었다. 이 발견으로, 무리수가 존재한다는 사실을 감추려고 애쓸(인류 역

사상 반복되는 무익한 행동방식) 정도로 피타고라스 학파들의 세계관은 혼란스럽게 되었다.

가장 오래된 정수론적인 알고리즘 두 개는 그리스의 수학자인 유클리드(Euclid)[B.C. 3세기]와 에

라토스테네스(Eratosthenes)[B.C. 276-195]에게서 전해져 왔고, 그것들은 요즘 인터넷 통신 보안을

위해 사용하는 가장 최신의 암호 알고리즘에 가깝다. 유클리드 알고리즘(Euclidean Algorithm) 과

에라토스테네스의 체(sieve of Erathosthenes) 는 우리의 과업(암호화 프로그램)을 위해서도 둘 다

현대적이다. 그리고 그들의 이론과 응용을 이 책의 10.1절과 10.5절에서 살펴볼 것이다.

현대 정수론의 가장 중요한 창시자들은 페르마(Pierre de Fermat)[1601-1665], 오일러(Leonhard Euler)

[1707-1783], 르장드르(Adrien Marie Legendre)[1752-1833], 가우스(Carl Friedrich Gauss) [1777-1855),

그리고 쿠머(Ernst Eduard Kummer)[1810-1893]로 볼 수 있다. 그들의 업적으로 인해, 수학에서의

정수론 분야를 현대 정수론으로 이끄는 초석이 만들어졌고, 특히 데이터를 암호화하고 전자서명을

만들기 위한 수학적인 과정으로 구성된, 암호학과 같은 흥미로운 응용 분야가 현대적으로 발전할

수 있는 기틀이 만들어졌다(16장 참조). 이외에도 이 분야에 중요한 공헌을 했던 사람들이 많다.

가령, 오늘날까지 정수론의 인상적인 발전에 영향을 끼치거나, 정수론의 역사와 그 주인공들을 기

술하는데 흥미를 가진 사람들이 있다. 필자는 진심으로 사이먼 싱(Simon Singh)이 저술한 페르

마의 마지막 정리(The Fermat 's Last Theorem) 를 추천한다.

우리는 이미 어렸을 때 어떤 것을 셈할 수 있다는 것을 배웠으며, 당연히 2 더하기 2는 4가 된다

는 것을 확신할 수 있다는 점을 깊이 있게 생각해 보자. 이런 단언에 대한 이론적인 증명을 하기

28

Page 10: C와 C++로 구현하는 암호화 알고리즘 · 2009-12-02 · 독일어문법때문에곤경에빠질때마다, 간단히사이버공간으로 질문을하면곧복호화할수있는알고리즘과함께Michael

소개

위해서는 놀라울 정도의 추상적인 생각까지 도달해야 한다. 예를 들면, 집합론(Set Theory)을 통해

서 아무것도 없는 것(공집합)으로부터 자연수가 존재한다는 것과 자연수의 산술 법칙을 이끌어 낼

수 있다. 아무것도 없다는 것은 공집합( : = {} )을 의미한다. 즉, 그 집합의 원소가 하나도

없다는 것이다. 공집합이 숫자 0과 관련이 있다는 사실을 깊이 있게 생각해 보자. 그러면 다음과

같은 집합들을 만들 수 있을 것이다. 0의 successor 0+는 0+ : = {0} = { }과 관련이 있고, 그

집합은 단 하나의 원소, 즉 공집합(null set)을 가지고 있다. 우리가 0의 successor의 이름을 1이라

고 한다면 이 집합에 대해서 뿐만 아니라 successor라는 것도 결정할 수 있다. 즉, 1+ : = { , { }}.

0과 1을 원소로 포함하는 1의 successor의 이름을 2라고 하자. 앞에서 한 것과 같이, 집합을 만들고

그 원소들을 하나씩 0, 1, 2라고 이름을 부여한다면, 이 집합은 자연수 0, 1, 2 원소를 가진 집합

과 동일하게 된다.

임의의 수 x가 자기 자신( x)과 이전의 집합을 합하여 successor x + : = x { x }를 형성하는

원리로, 다른 수들도 계속적으로 만들어 나갈 수 있다. 이런 식으로 0을 제외한 수 하나 하나가

모두 형성된다. 그리고 그것들은 그 자체로 predecessor를 구성하는 원소들의 집합이다. 오직 0만

이 predecessor가 없다. 이런 일련의 과정이 무한히 계속된다는 것을 증명하기 위해서, 집합론은

The axiom of inf inity 라는 특별한 공리를 공식화했다. 그 공리의 내용은 0뿐만 아니라 모든 원

소들의 successor를 포함하는 집합이 존재한다는 것이다.

0부터 시작해서 모든 successor를 포함하는, 소위 successor set이 존재한다는 가정으로부터 집합

론은 가장 작은 successor 집합이며, 그 자체로 모든 successor의 부분집합인 집합 을 이끌어 낸

다. 가장 작은 집합이며, 유일하게 정의된 집합을 자연수 집합이라고 한다. 그리고 자연수 집합

은 특별히 0을 하나의 원소로 포함한다.1)

successor : 바로 뒤의 원소

predecessor : 앞의 원소

successor set : 바로 뒤의 원소 집합

우리는 자연수를 직관적으로 이해할 수 있도록 도와주는 페아노(Giuseppe Peano)[1858-1932]의 공

리들로 자연수들의 특성을 기술할 수 있다.

1) 표준 DIN 5473에 따르면 0이 자연수에 속한다는 것은 확고한 것이 아니다. 그러나 컴퓨터 과학의 관점에서 1 대신에 0부터 숫

자 세기를 시작하는 것이 더 실용적이다. 그것은 덧셈의 항등원으로서 0이 중요한 역할을 하기 때문이다.

29

Page 11: C와 C++로 구현하는 암호화 알고리즘 · 2009-12-02 · 독일어문법때문에곤경에빠질때마다, 간단히사이버공간으로 질문을하면곧복호화할수있는알고리즘과함께Michael

PART I C로 구현하는산술 연산과정수론

⑴ 서로 같지 않은 두 자연수의 successor는 서로 같지 않다.

n m이면 n+ m+ (모든 n, m )

⑵ 0을 제외한 모든 자연수는 predecessor가 있다.     

+ = {0}

⑶ 완전한 수학적 귀납법(complete induction)의 원리 : 만약 S , 0 S, 그리고 n S

가 항상 n+ S를 의미한다면 S = 이다.

완전한 수학적 귀납법으로, 우리가 흥미로운 자연수의 산술 연산 법칙을 이끌어 낼 수 있다. 다음

과 같이 덧셈과 곱셈의 기본적인 연산을 귀납적으로 정의할 수 있다.

덧셈 연산부터 살펴보자.

에 속하는 모든 n(all n )에 대해 정의역이 이고, 치역이 인( ) Sn이라는 함수가 존재

한다.

⑴ Sn (0) = n,

⑵ Sn ( x+) = (Sn ( x))+ (모든 자연수 x )

Sn ( x) 함수의 결과값은 n과 x의 합인 n+ x이다. 그러나 모든 자연수 n(n )에 대해 함수 Sn이

존재하는지 여부를 증명해야 한다. 왜냐하면, 자연수들의 무한성(infinitude)이 그런 가정을 먼저

정의하지 않기 때문이다. 이 증명은 페아노의 세 번째 공리와 연관있는, 완전한 수학적 귀납법 원

리로 거슬러 올라가면 된다([Halm], 11-13장 참고).

앞에서와 같이 곱셈 연산에 대해 살펴보자.

모든 자연수 n(all n )에 대해 다음의 조건을 만족하는 정의역이 이고, 치역이 인( ) Pn이

라는 함수가 존재한다.

⑴ Pn (0) = 0,

⑵ Pn ( x+) = Pn ( x) + n (모든 자연수 x )

30

Page 12: C와 C++로 구현하는 암호화 알고리즘 · 2009-12-02 · 독일어문법때문에곤경에빠질때마다, 간단히사이버공간으로 질문을하면곧복호화할수있는알고리즘과함께Michael

소개

Pn ( x)의 결과값은 n x , n과 x의 곱이라고 한다. 짐작할 수 있듯이, 곱셈 연산은 덧셈 연산으로

정의된다. 페아노의 세 번째 공리에 따라 x에 완전한 수학적 귀납법 원리를 응용해서 산술 연산

으로부터 유명한 산술 연산 법칙인 교환 법칙(commutativity), 결합 법칙(associativity), 분배 법칙

(distributivity)을 정의할 수 있다([Halm], 13장 참고). 보통, 이런 법칙들을 당연하게 여기며 사용

하지만, 이런 법칙들은 FLINT 함수들을 테스트할 때 많은 도움이 될 것이다(12장과 17장 참고).

같은 방법으로, 지수 연산(expone nt iat ion)을 정의할 수 있다. 다음의 설명에서 이 연산이 중요하

다는 것을 한번 더 알 수 있을 것이다.

모든 자연수 n(all n )에 대해 다음의 조건을 만족하는 정의역이 이고, 치역이 인( )

e n이라는 함수가 존재한다.

⑴ e n (0) = 1,

⑵ e n ( x + ) = e n (x ) n 모든 자연수 x (all x )

함수 e n (x )의 값은 n을 x번 거듭제곱한 n x

완전한 수학적 귀납법으로 지수 법칙을 증명할 수 있다.

n x n y = n x + y , n x m x = ( n m ) x , ( n x ) y = n xy

좀 더 자세한 내용은 6장에서 살펴볼 것이다.

산술 연산에 덧붙여, 자연수 집합은 자연수 n , m ( n , m )을 비교할 수 있는 비교 연산자(order

relation)( < )를 정의할 수 있다. 이 사실은, 집합론적인 관점에서는 가치가 있을지라도, 매일매일

의 일상생활에서 사용하고, 잘 알고 있는 비교 연산자의 속성과 관련해서는 아무런 의미를 주지

못한다.

우리는 공집합을 자연수의 유일하고 근본적인 구성요소로 확립하면서 시작했기 때문에, 이제부터

그것들과 관계있는 요소들을 고려해 보자. 정수론이 일반적으로 자연수와 정수를 다루고, 지나치

게 에두름없이 그것들의 성질과 특성들을 다룬다고 할지라도, mathematical cell division 과정,

즉 자연수들뿐만 아니라 여기서 깊게 다루었던 연산들과 산술 법칙들을 만드는 과정을 힐끗 보는

것은 전혀 우리의 관심사가 아니다.

3 1

Page 13: C와 C++로 구현하는 암호화 알고리즘 · 2009-12-02 · 독일어문법때문에곤경에빠질때마다, 간단히사이버공간으로 질문을하면곧복호화할수있는알고리즘과함께Michael

PART I C로 구현하는산술 연산과정수론

소프트웨어에 대해서

이 책에 수록되어 있는 소프트웨어는 레퍼런스(reference)로 만들어지는 함수 라이브러리, 즉 패키

지(package) 형식으로 구성되었다. 이 라이브러리의 이름은 FLINT/C이다. 그 뜻은 정수론과 암

호화에 쓰이는 큰 정수(large integers)에 관한 함수들 이란 것이다.

FLINT/C 라이브러리는 다음의 모듈(modules)들을 포함하고 있다. 그리고 CD-ROM에 그 모듈들

의 소스 코드가 첨부되어 있다.

[표 1.1] flint/src 디렉토리에 산술 이론과 정수론을 구현한 C 라이브러리

flint.h flint.c에서 쓰이는 함수들의 원형을 정의한헤더파일

flint.c 산술 이론과정수론을구현한C 함수들

kmul.h,c Karatsuba 곱셈과나눗셈을 구현한함수들

ripemd.h,c RIPEMD-160 해시 함수의 구현

[표 1.2] flint/src 디렉토리에 산술 이론과 정수론을 구현한 C+ + 라이브러리

flintpp.h flint.cpp에서 함수들의 원형을 정의한헤더파일

flintpp.cpp 산술 이론과정수론을C++로구현한 함수들. 모듈은 flint.c에 있는 함수들을사용한다.

[표 1.3] flint/src/asm 디렉토리에 80x86 어셈블러로 구현한 산술 라이브러리(18장 참조)

mult.asm flint.c에서 mult()라는 함수를 어셈블러로구현한 곱셈 함수

umul.asm c 함수 umul()을 어셈블러로 구현한곱셈 함수

sqr.asm c 함수 sqr()을 어셈블러로 구현한제곱함수

div.asm c함수 div_l()을 어셈블러로 구현한 나눗셈 함수

32

Page 14: C와 C++로 구현하는 암호화 알고리즘 · 2009-12-02 · 독일어문법때문에곤경에빠질때마다, 간단히사이버공간으로 질문을하면곧복호화할수있는알고리즘과함께Michael

소개

[표 1.4] flint/lib 디렉토리에 80x86 어셈블러로 구현한 산술 라이브러리(18장 참조)

flinta.lib OMF(Object Module Format)의 어셈블러함수 라이브러리

flintavc.lib COFF(Common Object File Format)의 어셈블러함수 라이브러리

flinta.a OS/2 환경에서 emx/gcc를 지원하는 어셈블러함수 아카이브(archive)

llibflint.a 리눅스환경에서사용할수 있는 어셈블러함수 아카이브

[표 1.5] flint/test 디렉토리에 테스트 프로그램(12.2절과 17장 참조)

testxxx.c[pp] C와 C++ 테스트 프로그램

[표 1.6] flint/rsa 디렉토리에 RSA 구현(16장 참조)

rsakey.h RSA 클래스의헤더파일

rsakey.cpp RSAkey와 RSApub 클래스를구현한파일

rsademo.cpp RSA 클래스와그 함수들의 데모 프로그램

FLINT/C 소프트웨어의 각각의 구성요소들의 목록은 CD-ROM 안의 readme.doc 파일에 있다. FLINT/C

소프트웨어는 다음과 같은 플랫폼에서 쓰이는 개발도구로 검증했다.

리눅스, SunOS 4.1과 썬 솔라리스(Sun Solaris) 환경에서의 GNU gcc

OS/2 Warp, 도스(DOS) 그리고 윈도우즈(9x, NT) 환경에서의 GNU/EMX

윈도우즈(9x, NT, 2000) 환경에서의 lcc-win32

윈도우즈(9x, NT, 2000) 환경에서의 Cygnus cygwin B20

OS/2 Warp과 윈도우즈(9x, NT, 2000) 환경에서의 IBM VisualAge

도스(DOS), OS/2 Warp, 윈도우즈(9x, NT) 환경에서의 Microsoft C

윈도우즈(9x, NT, 2000) 환경에서의 Microsoft Visual C/C++

도스(DOS), OS/2 Warp, 윈도우즈(3.1, 9x, NT) 환경에서의 Watcom C/C++

33

Page 15: C와 C++로 구현하는 암호화 알고리즘 · 2009-12-02 · 독일어문법때문에곤경에빠질때마다, 간단히사이버공간으로 질문을하면곧복호화할수있는알고리즘과함께Michael

PART I C로 구현하는산술 연산과정수론

어셈블러 프로그램들은 Microsoft MASM2) 또는 Watcom WASM과 호환된다. CD-ROM에 OMF

(Object Module Format) 라이브러리로 변환된 형식이 수록되어 있고, 또 COFF(Common Object

File Format) 뿐만 아니라 리눅스 아카이브(Archive) 형식으로도 수록되어 있다. 그리고 C 프로그

램을 변환하려고 할 때 매크로 FLINT_ASM이 정의되어 있다면, 어셈블러 프로그램에서 그에 대

응하는 C 함수 대신에 어셈블러 함수가 사용될 수 있으며, 각각의 아카이브에 따라 라이브러리로

부터 어셈블러 객체 모듈들이 링크된다.

전형적인 컴파일러 호출(여기에서는 GNU 컴파일러 gcc)은 다음과 같다((압축이 풀린) 소스 디렉

토리 경로에서).

gcc - 02 - DFLINT_ASM- o r sademo r sademo .cpp r sakey .cpp

f l i nt . cpp f l i nt . c r i pemd . c - l f l i nt - l st dc++

ANSI 표준을 따르는 C++ 헤더 파일들은 컴파일 시에 FLINTPP_ANSI라는 매크로가 정의될 때

만 사용되고, 그렇지 않으면 일반적인(traditional) 헤더 파일들(xxxxx.h)이 사용된다.

컴퓨터 플랫폼에 따라 그 플랫폼에 더 알맞는 컴파일러가 있겠지만, 최고의 성능을 위해서는 속도

최적화 컴파일 옵션을 항상 선택해야 한다. 스택의 수용성(demands) 때문에, 많은 환경과 응용 프

로그램에서 속도 최적화 옵션은 적절히 조정되어야 한다.3) 특정한 응용 프로그램에서 필요한 스

택 크기를 언급하자면, 6장의 지수 연산 함수들과 [표 6.5]에 나와 있는 목록에 대한 제안들을 주

목해야 한다. 동적으로 스택 할당하는 지수 연산 함수를 사용하고, 또한 동적 레지스터를 이용하

면서 스택 요구사항에 대해 알 수 있을 것이다(9장 참조).

C 함수들과 상수들(constants)은 다음의 매크로들과 함께 제공된다.

_FLINT_API C 함수들의 한정자(qualifier)

_FLINT_API_A 어셈블러 함수들의 한정자(qualifier)

_FLINT_API_DATA 상수들의 한정자(qualifier)

2) 호출 : ml / Cx / c / Gd <파일 이름>3) DOS의 경우를 제외하고, 가상 메모리가 있는 요즘 컴퓨터에 대해 이런 점을 걱정할 필요는 없다. 특히, Unix 또는 리눅스 시스템

34

Page 16: C와 C++로 구현하는 암호화 알고리즘 · 2009-12-02 · 독일어문법때문에곤경에빠질때마다, 간단히사이버공간으로 질문을하면곧복호화할수있는알고리즘과함께Michael

소개

ext er n i nt __FLINT_API add_l (CLINT, CLINT, CLINT) ;ext er n USHORT __FLINT_API_DATA smal l pr i mes [] ;

또는, 각각의 어셈블러 함수들의 사용

ext er n i nt __FLINT_API_A di v_l (CLINT, CLINT, CLINT, CLINT) ;

이 매크로들은 일반적으로 /**/ 빈 주석으로 정의된다. 이 매크로들의 도움으로 적합한 정의를 할

수 있고, 함수들과 데이터들에 대해서 컴파일러와 링커(linker)에 특별한 지시를 할 수 있다. 만약

어셈블러 모듈을 사용하고 있는데, _FLINT_API_A라는 매크로를 _cdecl이라고 정의한 GNU gcc

컴파일러를 사용하지 않는다면, 어떤 컴파일러들은 그것을 C 함수의 이름과 같은 어셈블러 함수로

전환해서 호출하라는 지시문으로 이해할 것이다.

Microsoft Visual C/C++ 환경에서 DLL(Dynamic Link Library)로부터 FLINT/C 함수들과 상수들

을 가져오는 모듈은 번역 시 매크로들, -D_FLINT_API =_cdecl과 = -D_FLINT_API_DATA =

_declspec(dllimport)로 정의해야 한다. 이런 내용은 flint.h에 이미 감안되어 있고, 그것들은 컴파

일 시에 FLINT_USEDLL 매크로를 정의하는 경우에 충족된다. 다른 개발환경에서는 비슷한 정의

들을 따로 정의하고 사용해야 한다.

FLINT/C DLL을 초기화하는 작업 중에서 작은 부분을 FLINTInit_l()이라는 함수가 할 것이다. 이 함수

는 랜덤수4) 생성을 위해 초기 변수들을 설정하고, 동적인 레지스터들을 생성한다(9장 참조). 이와 반

대 역할을 하는 FLINTExit_l() 함수는 동적으로 생성한 레지스터들을 소멸한다. 현명하게, 초기화 과

정은 DLL을 사용하는 각각의 프로세스들이 다루지 않고 오직 DLL을 시작하는 곳에서만 초기화해야

한다. 일반적으로, 생성적인 특성을 가진, 그리고 호출 전환하는 함수, 런타임 시 DLL이 로드되었을

때 자동적으로 실행되는 함수가 사용되어야 한다. 이런 함수는 FLINT/C 초기화를 인수할 수 있고 앞

에서 언급했던 두 함수를 사용할 수 있다. 이 모든 것을 DLL이 생성될 때 고려해야 한다.

많은 사람들이 철저한 보안이 필요한 응용 프로그램에서 이 소프트웨어를 사용할 수 있도록 만드

는데 노력하고 있다. 끝으로, 보안 모드상에서, 특히 CLINT와 LINT 객체의 함수에서 지역 변수

를 사용한 후에 0으로 덮어쓰고 삭제한다. C 함수에서는 이런 것들이 매크로 PURGEVARS_L()

과 함수 purgecars_l()을 통해서 지원하고 있다. C++ 함수에서는 ~LINT() 소멸자에 이와 같은 내

4 ) 초기값들은 시스템 클락으로부터 가져온 32-비트수들로 이루어진다. 보안에 매우 민감한 응용 프로그램에 대해서는 충분히 큰, 시

스템 클락의 구간값으로부터 가져온 랜덤값을 초기값으로 사용하는 것이 좋다.

35

Page 17: C와 C++로 구현하는 암호화 알고리즘 · 2009-12-02 · 독일어문법때문에곤경에빠질때마다, 간단히사이버공간으로 질문을하면곧복호화할수있는알고리즘과함께Michael

PART I C로 구현하는산술 연산과정수론

용이 첨가되어 있다. 어셈블러 함수에서는 사용한 메모리를 다시 덮어씀으로써 이런 것들을 지원

한다. 단, 인자로 넘겨진 변수들의 삭제는 호출한 함수쪽에서 관리하기로 한다. 만약 특정한 시간

이 오래 걸리는 변수들의 삭제를 생략하려고 한다면 FLINT_UNSECURE라는 매크로를 정의해야

한다. 런타임 시 char* verstr_l()이라는 함수가 컴파일할 때 설정한 모드들에 대한 정보를 준다.

추가적으로 버전 라벨(version label) X.x에 보안 모드를 선택했다면 s 라는 문자가, 어셈블러를

지원했다면 a 라는 문자가 덧붙여 출력된다.

프로그램 사용의 합법적인 조건

본 소프트웨어는 개인적인 사용에 대해서는 전면적으로 공짜이다. 또한, 다음의 조건하에 프로그

램이 사용되거나 변경되거나 배포되었을 시에만 책임을 묻지 않는다.

1. 저작권이 변경되거나 삭제되지 않는다.

2. 모든 변경은 주석을 달아야 한다. 소프트웨어를 상업적인 목적 또는 그밖의 목적으로 사용

할 때는 반드시 저자나 출판사의 허락을 받는다.

본 소프트웨어는 심혈을 기울여서 만들고 검증했다. 그렇지만 에러란게 완벽하게 제거될 수 없기

때문에, 저자나 출판사도 위에 명시한 조건과 별개로 소프트웨어를 사용했을 때, 직접 또는 간접

적으로 입은 피해에 대해서는 책임지지 않는다.

저자와의 연락

저자는 에러나 그외의 다른 도움이 되는 조언과 코멘트를 기꺼이 즐겨받는다.

본 저자와 연락하려면 [email protected]으로 연락주십시요.

36

Page 18: C와 C++로 구현하는 암호화 알고리즘 · 2009-12-02 · 독일어문법때문에곤경에빠질때마다, 간단히사이버공간으로 질문을하면곧복호화할수있는알고리즘과함께Michael

2수체계 : C에서의 큰 정수들에 대한

설명

그래서 나는 큰 정수들을 쓰기 위해 나 자신의 시스템을 만들었고, 그 시스템을 설명할 때

는 이 장을 이용할 것이다.

- 아이작 아시모프(Isaac As imov) , Add ing a Dim e ns io n

이 방식을 더 높은 체계로 이끄는 과정도 또한 다르게 가정될 수 있다.

- 베버(J . Weber) , Fo rm ,Mot io n ,Co lo r

Page 19: C와 C++로 구현하는 암호화 알고리즘 · 2009-12-02 · 독일어문법때문에곤경에빠질때마다, 간단히사이버공간으로 질문을하면곧복호화할수있는알고리즘과함께Michael

PART I C로 구현하는산술 연산과정수론

큰 정수(large number) 함수 라이브러리를 만들기 위한, 맨 처음 단계 중 하나는 얼마나 큰 정수

들을 컴퓨터의 메인 메모리에 기술할 수 있는지를 결정짓는 것이다. 이것은 신중하게 정하는 게

중요하다. 왜냐하면, 이 시점에서 정한 결정은 나중에 수정하기가 어려워지기 때문이다. 소프트웨

어 라이브러리의 내부 구조를 바꾸는 것은 항상 가능한 일이지만, 사용자 인터페이스는 호환성이

라는 관점에서 가능한 한 견고하게 유지되어야 한다.

그리고 수치값들을 코딩하는데 사용할 데이터 타입과 처리할 숫자들의 크기별로 순서를 정해야

한다.

FLINT/C 라이브러리에서 모든 루틴들의 기본 함수들은 표준 데이터 타입의 수용 범위를 넘는, 700

자리 자연수의 처리 과정을 지원한다. 따라서, 우리는 큰 정수들을 표현할 수 있고, 계산할 수 있는

컴퓨터 메모리 단위들을 논리적으로 배열해야 한다. 이것에 관해서, 어떤 이들은 숫자값들을 표현

하기 위한 충분한 공간을 자동적으로 생성하는 구조체들을 상상할 지도 모르지만, 사실상 필요한

것은 단지 그런 것이 아니다. 어떤 이들은 큰 정수를 위한 산술 과정 중에서 필요에 따라서 메모

리를 할당하고 해제하는, 동적으로 메모리를 관리하는 메인 메모리 측면에서 경제적으로 구성된

하우스키핑(housekeeping)을 유지하고 싶어할 것이다. 그러나 그런 것들이 확실하게 구현될 지라

도 메모리 관리를 위한 연산 시간이 오래 걸린다. 이러한 이유로, FLINT/C 패키지에서는 정수를

나타낼 때 정적인 길이를 가진 더 간단한 개념들을 선호한다.

어떤 이들은 큰 자연수를 표현하기 위해서 표준 데이터 타입을 요소로 가지는 배열을 사용할지

모른다. 그러나 효율성이라는 측면에서는 부호없는(unsigned) 데이터 타입을 선호한다. 부호없는 데

이터 타입은 산술 연산의 결과값을, 예를 들면 산술 표준 C 데이터 타입 중에서 가장 큰 unsinged

long형(flint.h에 ULONG형)과 같이 손실없이 저장할 수 있기 때문이다([Harb], 섹션 5.1.1 참조). 그

리고 ULONG형 변수는 보통 CPU 레지스터 WORD와 정확하게 매치되기 때문이다.

우리의 목표는 가능한 한 CPU의 산술 레지스터에 직접 매치되어서 큰 정수들의 연산 과정이 컴

파일러에 의해 줄어들게 하는 것이다. 왜냐하면, 그런 것들은 말하자면, 컴퓨터가 곧바로 계산하는

부분들이기 때문이다. 결국, FLINT/C 패키지에서는 큰 정수를 표현하기 위해서 unsigned short int 데

이터형(USHORT)을 사용한다. USHORT 데이터형은 16비트로 표현되고 ULONG 데이터형은 USHORT

형들을 가지고 산술 연산의 결과값을 완전하게 수용할 수 있다고 가정한다. USHORT형과 ULONG형

의 비공식적으로 형성된 크기 관계는 USHORT×USHORT ULONG이다.

38

Page 20: C와 C++로 구현하는 암호화 알고리즘 · 2009-12-02 · 독일어문법때문에곤경에빠질때마다, 간단히사이버공간으로 질문을하면곧복호화할수있는알고리즘과함께Michael

수체계 : C에서의 큰 정수들에 대한 설명

이런 가정들이 특별한 컴파일러에서 수용될지 안 될지는 ISO 헤더 파일 limits.h으로부터 알 수

있을 것이다([Harb], 섹션 2.7.1 그리고 섹션 5.1 참조). 예를 들면, limits.h에 GNU C/C++ 컴파일

러를 위해서([Stlm] 참조) 다음과 같은 것들이 정의되어 있는 것을 볼 수 있을 것이다.

#def i ne UCHAR_MAX 0xf fU

#def i ne USHORT_MAX 0xf f f fU#def i ne UINT_MAX 0xf f f f f f f fU

#def i ne ULONG_MAX 0xf f f f f f f fUL

사실상, 이진수의 관점에서 구별할 수 있는 크기는 오직 세 가지가 있다는 것에 주목해야 한다.

USHRT형(USHORT)은 16-비트 레지스터를 표현할 수 있다. ULONG형은 32-비트 레지스터를 가

지고 CPU의 WORD 크기를 표현할 수 있다. ULONG_MAX형은 스칼라(scalar)형들로 표현할 수

있는 가장 큰 부호없는 수들의 모든 값을 결정한다([Harb], 110페이지 참조)1). USHRT형 두 개의

수를 곱한 값은 대략 0xffff * 0xffff = 0xfffe0001이고, 이 값은 ULONG형으로 표현할 수 있다.

ULONG형에서 가장 작은 단위의 16비트들(예를 들면, 0x0001)은 캐스트 연산자를 통해서 USHRT형

으로 분리할 수 있다. FLINT/C 패키지에서 기본적인 산술 함수들은 앞에서 논의한 USHORT형과

ULONG형 사이의 크기 관계에 의거해서 구현했다.

32-비트와 64-비트 크기 데이터 타입들을 앞에서 논의했던 USHORT형과 ULONG형처럼 유사하

게 사용하는 접근방식은 곱셈, 나눗셈과 지수 연산의 계산 시간을 약 25%정도 감소시킬 수 있다.

이런 가능성은 나눗셈과 곱셈을 위해 기계 명령어의 64-비트 결과값을 직접 접근하는 어셈블러

함수들로, 또는 ULONG형에 그런 결과들을 손실없이 저장할 수 있도록 C 구현을 지원하는 64-비

트 레지스터를 가진 프로세서들로 실현될 수 있다. FLINT/C 패키지에 산술 어셈블러 함수를 통

해서 계산 속도에 이득을 얻을 수 있는 몇 가지 예들이 있다(18장 참조).

다음으로, 중요한 문제는 배열에서 USHORT형 자리수들의 순서에 관한 것이다. 우리는 두 가지

가능성을 생각할 수 있다. 왼쪽에서 오른쪽으로, 낮은 메모리 주소에서 높은 메모리 주소로 자리

수가 낮아지는 방향 또는 그와는 반대로 오른쪽에서 왼쪽으로, 낮은 메모리 주소에서 높은 메모리

주소로 자리수가 높아지는 방향. 우리의 보통 표시법과는 반대인, 후자의 배열방식이, 정해진 주소

공간에서 숫자들의 크기가 변할 때 메모리에서 숫자들을 재배치하지 않고 추가적인 자리수를 단

순하게 할당하면 되기 때문에, 앞의 방식보다 유리하다. 따라서 선택은 이미 정해졌다. 숫자 표현

1) GNU C/ C+ +과 다른 C 컴파일러에서 us igned long long 같은 표준적이지 않은 데이터 타입들을 고려하지 않음

39

Page 21: C와 C++로 구현하는 암호화 알고리즘 · 2009-12-02 · 독일어문법때문에곤경에빠질때마다, 간단히사이버공간으로 질문을하면곧복호화할수있는알고리즘과함께Michael

PART I C로 구현하는산술 연산과정수론

방식에서의 자리수는 메모리 주소가 커질수록 또는 배열의 인덱스가 커질수록 커지는 것으로 정

한다.

숫자 하나를 원소로 더 추가하면 그 숫자는 배열의 첫 번째 원소로 추가되고 저장된다. 따라서

메모리에서 큰 정수들의 표현은 다음과 같은 형식을 가진다.

n = ( ln 1n 2 . . . n l) B , 0 l CLINTMAXDIGIT, 0 n i B , i = 1, . . . , l,

여기서 B는 숫자의 밑수(base)를 나타낸다. FLINT/C 패키지에서 B := 2 16= 65536이다. B의 값은

여기서부터 우리의 동무가 될 것이고, 앞으로도 계속 나올 것이다. 상수 CLINTMAXDIGIT은 CLINT

객체가 나타낼 수 있는 숫자의 최대 자리수를 나타낸다.

0(제로)은 길이 l = 0임을 나타낸다. FLINT/C 변수 n_l로 표현되는 숫자 n의 값은 다음과 같이

계산된다.

만약 n >0, 베이스 B에 n의 가장 작은 자리수의 값은 n_l[1]로 주어지고, 가장 큰 자리수의 값은

n_l[n_l[0]]으로 주어진다. n_l[0] 자리수의 값은 매크로 DIGITS_L(n_l)를 통해서 알 수 있을 것이

고, 매크로 SETDIGITS_L (n_l, l)에 의해서 l값으로 설정할 수 있다. 마찬가지로, 가장 작은 자리

수의 값은 LSDPTR_L(n_l)에 의해서, 그리고 가장 큰 자리수의 값은 MSDPTR_L(n_l)에 의해서

전달된다. 앞의 두 매크로는 그 자리수의 포인터를 반환한다. flint.h에 정의된 매크로들은 실제로

숫자들이 표현되는 것에는 관여하지 않는다.

자연수들의 부호가 필요없기 때문에, 이제 우리가 사용하려고 하는 수를 표현하기 위한 모든 요소

들을 갖추었다. 다음과 같이 데이터 타입을 정의한다.

t ypedef uns i gned shor t cl i nt ;

t ypedef cl i nt CLINT[CLINTNMAXDI GI T+1] ;

40

Page 22: C와 C++로 구현하는 암호화 알고리즘 · 2009-12-02 · 독일어문법때문에곤경에빠질때마다, 간단히사이버공간으로 질문을하면곧복호화할수있는알고리즘과함께Michael

수체계 : C에서의 큰 정수들에 대한 설명

큰 수는 다음과 같이 선언할 것이다.

CLINT n_l ;

CLINT형 함수 인자를 선언하려면 CLINT n_l식으로 사용하면 된다.2) CLINT 객체로 포인터 myptr_l

을 선언하려면 CLINTPTR myptr_l 또는 CLINT *myptr_l식으로 사용하면 된다.

flint.h에 있는 CLINTMAXDIGIT에 따르면 FLINT/C 함수들은 4096 비트수까지 처리할 수 있다.

10진수로는 1233자리, 또는 베이스 2 16에서는 256자리까지 처리할 수 있다. CLINTMAXDIGIT를

변경해서 최대의 길이를 조정할 수 있다. 그리고 다른 상수들의 정의는 다음과 같다. 예를 들면,

CLINT 객체에서 USHORT형의 길이는 다음과 같이 정의된다.

#def i ne CLINTMAXSHORT CLINTMAXDI GI T +1

그리고 처리할 수 있는 이진 자리의 최대 길이는 다음과 같이 정의된다.

#def i ne CLINTMAXBIT CLINTMAXDI GI T << 4

상수 CLINTMAXDIGIT와 CLINTMAXBIT는 자주 사용되지만 인쇄상으로는 잘 안 쓰이기 때문

에, 이 상수들을 약어 MAXB와 MAX2로 나타낸다(프로그램 코드에서는 예외). 정의에 따르면 CLINT

객체들은 [0 , BM A X B - 1], [0 , 2

M A X 2 - 1] 사이의 값으로 가정될 수 있다. N m ax 에 의해, CLINT 객

체에 의해 표현될 수 있는 최대의 자연수, BM A X B - 1 = 2

M A X 2 - 1을 의미한다.

몇 가지 함수들은 CLINT 객체가 표현할 수 있는 자리수보다 좀 더 많은 자리수를 갖는 수가 필

요하다. 이런 경우를 위해 CLINT형의 변형을 다음과 같이 정의한다.

t ypedef uns i gned shor t CLINTD[1+(CLINTMAXDI GI T<<1)] ;

그리고

t ypedef uns i gned shor t CLINTQ[1+(CLINTMAXDI GI T<<2)] ;

2) [Lind]라는 책의 4장과 9장을 참고하면 좋다. 그곳에는 C에서 배열과 포인터가 같을 때, 그리고 다를 때, 그것을 잘못 알고 있기

때문에 생길 수 있는 에러들에 대한 설명이 나와 있다.

4 1

Page 23: C와 C++로 구현하는 암호화 알고리즘 · 2009-12-02 · 독일어문법때문에곤경에빠질때마다, 간단히사이버공간으로 질문을하면곧복호화할수있는알고리즘과함께Michael

PART I C로 구현하는산술 연산과정수론

앞에서 정의된 데이터 타입은 CLINT형보다 2배, 4배의 자리수를 수용할 수 있다.

flint.c에서 프로그래밍의 편의를 도모하기 위해, CLINT형 표현으로 0, 1, 그리고 2를 나타내는 상

수 nul_l, one_l, 그리고 two_l을 정의해 놓았다. 그리고 flint.h에, CLINT 객체가 0, 1, 2값을 갖도

록 만드는 매크로 SETZERO_L(), SETONE_L(), 그리고 SETTWO_L()을 정의해 놓았다.

42