<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Math on kenji.blog</title><link>http://kenji.blog/ko/tags/math/</link><description>Recent content in Math on kenji.blog</description><generator>Hugo -- gohugo.io</generator><language>ko</language><copyright>kenjinote</copyright><lastBuildDate>Fri, 11 Sep 2026 22:00:00 +0900</lastBuildDate><atom:link href="http://kenji.blog/ko/tags/math/index.xml" rel="self" type="application/rss+xml"/><item><title>【초보자용】 Transformer 모델의 수학적 구조 파헤치기</title><link>http://kenji.blog/ko/p/transformer-mathematical-structure/</link><pubDate>Fri, 11 Sep 2026 22:00:00 +0900</pubDate><guid>http://kenji.blog/ko/p/transformer-mathematical-structure/</guid><description>&lt;img src="http://kenji.blog/p/transformer-mathematical-structure/img/eyecatch.jpg" alt="Featured image of post 【초보자용】 Transformer 모델의 수학적 구조 파헤치기" />&lt;h1 id="머리말-왜-transformer의-수학을-배우는가">머리말: 왜 Transformer의 수학을 배우는가?
&lt;/h1>&lt;p>현대 자연어 처리(NLP), 그리고 AI 전체의 역사를 새로 썼다고 해도 과언이 아닌 아키텍처가 바로 &amp;lsquo;Transformer&amp;rsquo;입니다. 2017년 Google 연구진이 발표한 논문 『Attention Is All You Need』에서 처음 제안된 이 모델은, OpenAI의 GPT 시리즈(ChatGPT의 기반 기술)나 Google의 BERT, Anthropic의 Claude 등 현재 세계를 휩쓸고 있는 대규모 언어 모델(LLM)의 심장부로 기능하고 있습니다.&lt;/p>
&lt;p>하지만 Transformer의 원리에 대해 &amp;lsquo;Attention(주의 메커니즘)을 사용하여 문맥을 이해한다&amp;rsquo;와 같은 정성적인 설명은 자주 볼 수 있지만, 그 이면에 있는 &lt;strong>수학적인 구조&lt;/strong>에 대해 초보자를 위해 깊이 파고든 해설은 의외로 적은 것이 현실입니다. AI가 어떻게 &amp;lsquo;언어&amp;rsquo;를 &amp;lsquo;수식&amp;rsquo;으로 처리하고 놀라울 정도로 자연스러운 문장을 생성해 내는지 진정으로 이해하기 위해서는, 그 수학적 메커니즘을 파헤치는 것이 필수적입니다.&lt;/p>
&lt;p>본 문서에서는 수학이나 프로그래밍에 대한 기초 지식(고등학교 수준의 행렬이나 미분 개념을 아는 분)을 가진 분들을 대상으로, Transformer의 심장부인 &amp;lsquo;Self-Attention 메커니즘&amp;rsquo;, &amp;lsquo;쿼리·키·밸류(Q/K/V) 모델&amp;rsquo;, &amp;lsquo;Softmax 함수를 통한 정규화&amp;rsquo;, 그리고 &amp;lsquo;Positional Encoding&amp;rsquo; 등의 수학적 구조를 철저하고 알기 쉽게 풀어내고자 합니다.&lt;/p>
&lt;p>수식의 나열에 압도될 수도 있지만, 하나하나의 계산에는 명확한 &amp;lsquo;의미&amp;rsquo;가 있습니다. 이 글을 다 읽을 즈음에는 Transformer가 단순한 마법의 블랙박스가 아니라, 정교하게 설계된 수학과 통계의 결정체라는 것을 이해할 수 있을 것입니다.&lt;/p>
&lt;hr>
&lt;h1 id="1-기존-기법의-한계와-transformer의-혁신성">1. 기존 기법의 한계와 Transformer의 혁신성
&lt;/h1>&lt;p>Transformer가 등장하기 전, 자연어 처리의 주류는 순환 신경망(RNN)이나 그 파생형인 LSTM(Long Short-Term Memory)이었습니다. RNN은 시계열 데이터를 처리하기 위해 설계되었으며, 문장을 단어 단위로 처음부터 순서대로 읽어 들입니다.&lt;/p>
&lt;p>하지만 RNN에는 치명적인 약점이 두 가지 있었습니다.&lt;/p>
&lt;ol>
&lt;li>&lt;strong>장기 의존성 학습이 어려움&lt;/strong>: 문장이 길어지면 처음에 입력된 단어의 정보가 마지막에 도달할 때쯤이면 희미해집니다(기울기 소실 문제).&lt;/li>
&lt;li>&lt;strong>병렬 계산이 불가능함&lt;/strong>: 단어를 순서대로 처리해야 하므로, GPU를 이용한 대규모 병렬 계산이 어려워 학습에 막대한 시간이 걸립니다.&lt;/li>
&lt;/ol>
&lt;p>Transformer는 RNN의 구조를 완전히 버리고, 오직 &amp;lsquo;Attention&amp;rsquo;만을 사용하여 문맥을 파악한다는 패러다임 시프트를 일으켰습니다. 이를 통해 시퀀스 길이가 아무리 길어도 정보의 손실이 없으며, 연산을 병렬화하여 GPU의 성능을 극대화하는 것이 가능해진 것입니다.&lt;/p>
&lt;hr>
&lt;h1 id="2-transformer의-전체-아키텍처">2. Transformer의 전체 아키텍처
&lt;/h1>&lt;p>먼저 Transformer 전체의 아키텍처를 조감해 봅시다. Transformer는 크게 나누어 &amp;lsquo;Encoder(인코더)&amp;lsquo;와 &amp;lsquo;Decoder(디코더)&amp;lsquo;라는 두 개의 블록으로 구성되어 있습니다. 번역 작업을 예로 들면, Encoder가 입력 언어(예: 영어)를 수학적인 벡터 표현으로 변환하고, Decoder가 그 벡터 표현을 바탕으로 출력 언어(예: 한국어)를 생성합니다.&lt;/p>
&lt;p>아래 그림은 Encoder 블록의 내부 구조를 간략화한 것입니다.&lt;/p>
&lt;div class="mermaid">graph TD
A["입력 토큰(Input Tokens)"] --> B["입력 임베딩(Input Embedding)"]
B --> C["위치 인코딩(Positional Encoding)"]
C --> D["멀티 헤드 셀프 어텐션(Multi-Head Self-Attention)"]
D --> E["더하기 &amp; 층 정규화(Add &amp; Layer Normalization)"]
E --> F["피드 포워드 네트워크(Feed Forward Network)"]
F --> G["더하기 &amp; 층 정규화(Add &amp; Layer Normalization)"]
G --> H["다음 층으로 출력(Output to Next Layer)"]
C -.->|"잔차 연결(Residual Connection)"| E
E -.->|"잔차 연결(Residual Connection)"| G&lt;/div>
&lt;p>지금부터는 각 컴포넌트에서 수행되는 수학적인 조작을 순서대로 살펴보겠습니다.&lt;/p>
&lt;hr>
&lt;h1 id="3-단어의-벡터화와-위치-인코딩positional-encoding">3. 단어의 벡터화와 위치 인코딩(Positional Encoding)
&lt;/h1>&lt;p>컴퓨터는 텍스트를 있는 그대로 이해할 수 없습니다. 입력된 텍스트는 먼저 &amp;lsquo;토큰(Token)&amp;lsquo;이라는 단위로 분할되고, 각각이 고정 길이의 벡터로 변환됩니다. 이것이 &lt;strong>Input Embedding&lt;/strong>입니다.&lt;/p>
&lt;h2 id="31-input-embedding의-수학">3.1 Input Embedding의 수학
&lt;/h2>&lt;p>어휘(Vocabulary)의 크기를 $V$, 임베딩 벡터의 차원 수를 $d_{model}$(원본 논문에서는 $d_{model} = 512$)이라고 합시다. 각 단어 $w_i$ 는 임베딩 행렬 $W_E \in \mathbb{R}^{V \times d_{model}}$ 을 사용하여 벡터 $x_i \in \mathbb{R}^{d_{model}}$ 로 변환됩니다.&lt;/p>
$$ x_i = W_E \cdot \text{one\_hot}(w_i) $$
&lt;p>이에 따라 문장 전체는 행렬 $X \in \mathbb{R}^{N \times d_{model}}$ 로 표현됩니다($N$ 은 문장의 길이).&lt;/p>
&lt;h2 id="32-positional-encoding위치-인코딩의-필요성과-수식">3.2 Positional Encoding(위치 인코딩)의 필요성과 수식
&lt;/h2>&lt;p>Transformer는 RNN처럼 단어를 순서대로 처리하는 것이 아니라, 모든 단어를 동시에 병렬 처리합니다. 이는 계산 속도 측면에서는 큰 장점이지만, 동시에 &lt;strong>&amp;lsquo;단어의 순서(어순)&amp;lsquo;라는 중요한 정보가 손실되어 버린다&lt;/strong>는 문제를 야기합니다. 예를 들어 &amp;ldquo;개가 사람을 문다&amp;quot;와 &amp;ldquo;사람이 개를 문다&amp;quot;는 입력되는 단어 집합은 같지만 의미는 전혀 다릅니다.&lt;/p>
&lt;p>이 어순 정보를 모델에 제공하기 위해 고안된 것이 &lt;strong>Positional Encoding&lt;/strong>입니다.
위치 $pos$ 에 있는 단어의 $i$ 번째 차원에 대한 Positional Encoding $PE$ 는 다음 삼각 함수를 사용하여 계산됩니다.&lt;/p>
$$ PE_{(pos, 2i)} = \sin\left(\frac{pos}{10000^{2i/d_{model}}}\right) $$
$$ PE_{(pos, 2i+1)} = \cos\left(\frac{pos}{10000^{2i/d_{model}}}\right) $$
&lt;p>여기서 $pos$ 는 단어의 위치($0, 1, 2, \dots, N-1$), $i$ 는 벡터 차원의 인덱스($0, 1, \dots, d_{model}/2 - 1$)입니다.&lt;/p>
&lt;h3 id="왜-사인과-코사인을-사용할까">왜 사인과 코사인을 사용할까?
&lt;/h3>&lt;p>언뜻 보면 매우 복잡하고 기묘한 수식처럼 보이지만, 여기에는 깊은 수학적인 이유가 있습니다. 삼각 함수를 사용함으로써 모델은 &lt;strong>&amp;lsquo;절대적인 위치&amp;rsquo;뿐만 아니라 &amp;lsquo;상대적인 위치&amp;rsquo;의 차이&lt;/strong>를 쉽게 학습할 수 있게 되는 것입니다.&lt;/p>
&lt;p>고등학교 수학에서 배우는 삼각 함수의 덧셈 정리를 떠올려 보세요.
&lt;/p>
$$ \sin(\alpha + \beta) = \sin\alpha \cos\beta + \cos\alpha \sin\beta $$
$$ \cos(\alpha + \beta) = \cos\alpha \cos\beta - \sin\alpha \sin\beta $$
&lt;p>어떤 위치 $pos$ 에서 오프셋 $k$ 만큼 떨어진 위치 $pos + k$ 의 Positional Encoding은, 위치 $pos$ 의 Positional Encoding의 선형 결합으로 표현할 수 있습니다. 즉, 행렬 $M_k$ 를 사용하여 다음과 같이 쓸 수 있습니다.&lt;/p>
$$ PE_{pos+k} = M_k \cdot PE_{pos} $$
&lt;p>이를 통해 Attention 메커니즘은 단어끼리 &amp;lsquo;얼마나 떨어져 있는지&amp;rsquo;라는 상대적 거리를 내적 계산을 통해 쉽게 인식할 수 있게 됩니다. 또한, 파장이 다른 여러 사인/코사인 파동을 조합함으로써 아무리 긴 문장이라도 고유한 위치 벡터를 생성할 수 있다는 장점도 있습니다.&lt;/p>
&lt;p>최종적인 입력 행렬 $X_{input}$ 은 단어의 임베딩 벡터에 이 위치 인코딩을 더한 것이 됩니다.&lt;/p>
$$ X_{input} = X + PE $$
&lt;hr>
&lt;h1 id="4-self-attention셀프-어텐션-메커니즘의-심오한-수학">4. Self-Attention(셀프 어텐션 메커니즘)의 심오한 수학
&lt;/h1>&lt;p>드디어 Transformer의 가장 중요한 컴포넌트인 **Self-Attention(셀프 어텐션 메커니즘)**으로 들어갑니다. Self-Attention의 목적은 &amp;lsquo;문장 내의 모든 단어들 사이의 연관도를 계산하여, 각 단어의 벡터를 문맥을 고려한 더 풍부한 표현으로 갱신하는 것&amp;rsquo;입니다.&lt;/p>
&lt;p>여기서는 &amp;lsquo;검색 시스템&amp;rsquo;의 비유가 사용됩니다.&lt;/p>
&lt;ul>
&lt;li>&lt;strong>Query (Q)&lt;/strong>: 쿼리(검색어). &amp;ldquo;내가 지금 찾고 있는 정보는 무엇인가?&amp;rdquo;&lt;/li>
&lt;li>&lt;strong>Key (K)&lt;/strong>: 키(색인/제목). &amp;ldquo;내가 가지고 있는 정보는 무엇인가?&amp;rdquo;&lt;/li>
&lt;li>&lt;strong>Value (V)&lt;/strong>: 밸류(실체/내용). &amp;ldquo;내가 실제로 제공할 정보는 무엇인가?&amp;rdquo;&lt;/li>
&lt;/ul>
&lt;h2 id="41-행렬-q-k-v-의-생성">4.1 행렬 $Q, K, V$ 의 생성
&lt;/h2>&lt;p>입력 행렬 $X \in \mathbb{R}^{N \times d_{model}}$ (여기서는 설명을 간단히 하기 위해 배치 크기는 무시합니다)에 대하여 학습 가능한 가중치 행렬 $W^Q, W^K, W^V \in \mathbb{R}^{d_{model} \times d_k}$ 를 곱함으로써 쿼리 $Q$, 키 $K$, 밸류 $V$ 를 계산합니다. (통상적으로 $d_k = d_v = d_{model} / h$)&lt;/p>
$$ Q = X W^Q $$
$$ K = X W^K $$
$$ V = X W^V $$
&lt;p>여기서 $Q, K, V$ 는 모두 $\mathbb{R}^{N \times d_k}$ 의 행렬이 됩니다.&lt;/p>
&lt;h2 id="42-attention-score의-계산내적">4.2 Attention Score의 계산(내적)
&lt;/h2>&lt;p>각 단어의 Query가 다른 모든 단어의 Key와 얼마나 연관되어 있는지를 측정하기 위해 벡터의 &lt;strong>내적&lt;/strong>을 계산합니다. 행렬 연산으로 쓰면 다음과 같습니다.&lt;/p>
$$ \text{Scores} = Q K^T $$
&lt;p>이 계산을 통해 얻어지는 행렬 $\text{Scores} \in \mathbb{R}^{N \times N}$ 의 각 원소 $s_{ij}$ 는, $i$ 번째 단어의 Query와 $j$ 번째 단어의 Key의 내적, 즉 &amp;lsquo;연관도의 강도&amp;rsquo;를 나타냅니다.&lt;/p>
&lt;h2 id="43-스케일링scale">4.3 스케일링(Scale)
&lt;/h2>&lt;p>내적을 통한 점수 계산에는 한 가지 문제가 있습니다. 벡터의 차원 $d_k$ 가 커지면 내적의 값이 극단적으로 커지거나 작아져 버리는 것입니다.&lt;/p>
&lt;p>이를 수학적으로 증명해 봅시다.
쿼리의 각 원소 $q \sim \mathcal{N}(0, 1)$, 키의 각 원소 $k \sim \mathcal{N}(0, 1)$ 이 독립적인 표준 정규 분포를 따른다고 가정합니다.
내적 $q \cdot k = \sum_{i=1}^{d_k} q_i k_i$ 의 평균과 분산을 구합니다.
평균： $\mathbb{E}[q_i k_i] = \mathbb{E}[q_i] \mathbb{E}[k_i] = 0 \times 0 = 0$ 이므로, 합의 평균도 $0$.
분산： $q_i k_i$ 의 분산은 독립성에 의해 $\text{Var}(q_i k_i) = \mathbb{E}[(q_i k_i)^2] - (\mathbb{E}[q_i k_i])^2 = 1 \times 1 - 0 = 1$.
따라서 내적 전체의 분산은 차원 수 $d_k$ 와 같아집니다.&lt;/p>
$$ \text{Var}(q \cdot k) = d_k $$
&lt;p>분산이 커지면 이후에 적용할 Softmax 함수에서 최댓값 이외의 기울기가 극단적으로 작아지는 &amp;lsquo;기울기 소실(Gradient Vanishing)&amp;lsquo;이 발생하여 학습이 진행되지 않게 됩니다.
이를 방지하기 위해 점수를 $\sqrt{d_k}$ 로 나누어(스케일링하여) 분산을 항상 $1$ 로 유지하도록 합니다.&lt;/p>
$$ \text{Scaled Scores} = \frac{Q K^T}{\sqrt{d_k}} $$
&lt;h2 id="44-softmax-함수를-통한-확률화">4.4 Softmax 함수를 통한 확률화
&lt;/h2>&lt;p>얻은 점수를 합계가 $1$ 이 되는 확률 분포(가중치)로 변환하기 위해 &lt;strong>Softmax 함수&lt;/strong>를 행 단위로 적용합니다.&lt;/p>
$$ a_{ij} = \text{softmax}(s_i)_j = \frac{\exp(s_{ij} / \sqrt{d_k})}{\sum_{m=1}^N \exp(s_{im} / \sqrt{d_k})} $$
&lt;p>행렬 $A \in \mathbb{R}^{N \times N}$ 은 Attention Weight(주의 가중치) 행렬이라고 불립니다. 이 행렬의 각 행 $i$ 를 보면 &amp;ldquo;단어 $i$ 를 이해하는 데 있어, 다른 어떤 단어 $j$ 에 얼마나 주목(Attention)해야 하는가&amp;quot;가 0에서 1 사이의 값으로 표현되어 있습니다.&lt;/p>
&lt;h2 id="45-value의-가중합">4.5 Value의 가중합
&lt;/h2>&lt;p>마지막으로, 얻은 Attention Weight 행렬 $A$ 를 사용하여 Value 행렬 $V$ 의 가중합(Weighted Sum)을 계산합니다.&lt;/p>
$$ \text{Output} = A V = \text{softmax}\left(\frac{Q K^T}{\sqrt{d_k}}\right) V $$
&lt;p>이 연산을 통해 출력되는 행렬 $Z \in \mathbb{R}^{N \times d_v}$ 는 &amp;lsquo;문맥을 고려하여 갱신된 단어의 벡터 표현&amp;rsquo;들의 집합이 됩니다.
이것이 논문에서 정의하고 있는 &lt;strong>Scaled Dot-Product Attention&lt;/strong>의 전모입니다.&lt;/p>
&lt;hr>
&lt;h1 id="5-multi-head-attention멀티-헤드-어텐션">5. Multi-Head Attention(멀티 헤드 어텐션)
&lt;/h1>&lt;p>한 번의 Attention 계산(싱글 헤드)만으로는 하나의 관점(예를 들어 &amp;lsquo;문법적인 관계&amp;rsquo;)에서밖에 문맥을 포착하지 못할 가능성이 있습니다. 그래서 언어가 가진 다양한 의미적·구문적 관계(&amp;lsquo;주어와 서술어&amp;rsquo;, &amp;lsquo;대명사와 그 지시 대상&amp;rsquo; 등)를 동시에 파악하기 위해 &lt;strong>Multi-Head Attention&lt;/strong>이 도입되었습니다.&lt;/p>
&lt;p>앞서 말한 $Q, K, V$ 의 생성과 Attention 계산을 병렬로 $h$ 번(헤드의 수. 원본 논문에서는 $h=8$) 수행합니다.&lt;/p>
$$ \text{head}_i = \text{Attention}(X W_i^Q, X W_i^K, X W_i^V) $$
&lt;p>여기서 $W_i^Q, W_i^K, W_i^V \in \mathbb{R}^{d_{model} \times d_k}$ 는 $i$ 번째 헤드 전용의 학습 가능한 가중치 행렬입니다.&lt;/p>
&lt;p>각 헤드에서 출력된 결과 $\text{head}_i \in \mathbb{R}^{N \times d_v}$ 는 가로로 결합(Concatenate)됩니다.&lt;/p>
$$ \text{Concat}(\text{head}_1, \dots, \text{head}_h) \in \mathbb{R}^{N \times (h \cdot d_v)} $$
&lt;p>일반적으로 $h \cdot d_v = d_{model}$ 이 되도록 설정하기 때문에 결합 후의 차원은 다시 입력과 같은 $d_{model}$ 로 돌아옵니다. 마지막으로 이 행렬에 가중치 행렬 $W^O \in \mathbb{R}^{d_{model} \times d_{model}}$ 을 곱하여 최종적인 출력을 얻습니다.&lt;/p>
$$ \text{MultiHead}(Q, K, V) = \text{Concat}(\text{head}_1, \dots, \text{head}_h) W^O $$
&lt;div class="mermaid">graph TD
X["입력 X(Input X)"] --> Q1["Q1"]
X --> K1["K1"]
X --> V1["V1"]
Q1 &amp; K1 &amp; V1 --> H1["헤드 1(Head 1)"]
X --> Q2["Q2"]
X --> K2["K2"]
X --> V2["V2"]
Q2 &amp; K2 &amp; V2 --> H2["헤드 2(Head 2)"]
X --> QN["..."]
X --> KN["..."]
X --> VN["..."]
QN &amp; KN &amp; VN --> HN["헤드 h(Head h)"]
H1 &amp; H2 &amp; HN --> C["결합(Concatenate)"]
C --> WO["WO 곱하기(Multiply by WO)"]
WO --> OUT["멀티 헤드 출력(Multi-Head Output)"]&lt;/div>
&lt;hr>
&lt;h1 id="6-feed-forward-neural-network-ffn">6. Feed-Forward Neural Network (FFN)
&lt;/h1>&lt;p>Multi-Head Attention의 출력은 다음으로 **Position-wise Feed-Forward Network (FFN)**에 입력됩니다.
이는 시퀀스의 &amp;lsquo;각 위치(단어)마다 독립적으로&amp;rsquo; 적용되는 2층의 완전 연결 신경망(Fully Connected Neural Network)입니다.&lt;/p>
&lt;p>수식으로 나타내면 다음과 같습니다.&lt;/p>
$$ \text{FFN}(x) = \max(0, x W_1 + b_1) W_2 + b_2 $$
&lt;p>여기서 $\max(0, z)$ 는 ReLU(Rectified Linear Unit) 활성화 함수를 나타냅니다(최근의 모델에서는 GELU나 SwiGLU가 사용되는 경우도 많습니다).&lt;/p>
&lt;p>이 네트워크의 역할은 매우 중요합니다. Attention 메커니즘은 &amp;lsquo;단어 간의 관계성(공간적·순차적인 관계)&amp;lsquo;을 학습하지만, FFN은 &amp;lsquo;각 단어 벡터 자체의 비선형적인 특징 변환&amp;rsquo;을 담당합니다.
통상적으로 제1층의 가중치 $W_1$ 을 통해 차원을 일시적으로 크게 확대(예를 들어 $d_{model}=512$ 에서 $d_{ff}=2048$ 로 4배 확대)하여 특징 공간에서 복잡한 계산을 수행한 후, 제2층의 가중치 $W_2$ 로 다시 원래의 차원으로 되돌립니다. 이러한 &amp;lsquo;차원의 확대와 축소&amp;rsquo;를 통해 모델의 표현력이 비약적으로 향상됩니다.&lt;/p>
&lt;hr>
&lt;h1 id="7-잔차-연결residual-connection과-layer-normalization">7. 잔차 연결(Residual Connection)과 Layer Normalization
&lt;/h1>&lt;p>딥러닝에서 네트워크의 층을 깊게 쌓아가면 학습 시 기울기가 소실되거나 폭발해 버려 제대로 학습할 수 없게 되는 문제가 발생합니다. 이를 방지하기 위해 Transformer의 각 서브 레이어(Attention과 FFN) 주변에는 **잔차 연결(Residual Connection)**과 **Layer Normalization(층 정규화)**가 배치되어 있습니다.&lt;/p>
&lt;p>수식으로 쓰면 서브 레이어의 출력은 다음과 같이 처리됩니다.&lt;/p>
$$ \text{Output} = \text{LayerNorm}(x + \text{Sublayer}(x)) $$
&lt;h2 id="71-잔차-연결-x--textsublayerx">7.1 잔차 연결 ($x + \text{Sublayer}(x)$)
&lt;/h2>&lt;p>입력 $x$ 를 서브 레이어의 출력에 직접 더합니다. 이를 통해 역전파(Backpropagation) 시 기울기가 지름길(Shortcut)을 통해 얕은 층으로 직접 전달되므로 층이 깊어져도 학습이 안정됩니다.&lt;/p>
&lt;h2 id="72-layer-normalization의-수학">7.2 Layer Normalization의 수학
&lt;/h2>&lt;p>Layer Normalization은 특징 차원 방향의 평균과 분산을 계산하여 데이터를 정규화하는 기술입니다. 배치 크기 $B$, 시퀀스 길이 $N$, 차원 수 $d_{model}$ 의 입력에서 어떤 하나의 단어 벡터 $x \in \mathbb{R}^{d_{model}}$ 에 대해 정규화를 수행합니다.&lt;/p>
&lt;p>평균 $\mu$ 와 분산 $\sigma^2$ 을 계산합니다.
&lt;/p>
$$ \mu = \frac{1}{d_{model}} \sum_{i=1}^{d_{model}} x_i $$
$$ \sigma^2 = \frac{1}{d_{model}} \sum_{i=1}^{d_{model}} (x_i - \mu)^2 $$
&lt;p>그리고 정규화된 출력 $\hat{x}$ 를 얻습니다.
&lt;/p>
$$ \text{LN}(x) = \frac{x - \mu}{\sqrt{\sigma^2 + \epsilon}} \odot \gamma + \beta $$
&lt;p>
($\epsilon$ 은 0으로 나누는 것을 방지하기 위한 미소한 상수. $\gamma, \beta$ 는 학습 가능한 스케일 및 시프트 파라미터)&lt;/p>
&lt;p>배치 방향의 정규화(Batch Normalization)가 아닌 층 방향의 정규화(Layer Normalization)를 채택한 이유는, 문장처럼 길이가 일정하지 않은 시퀀스 데이터를 처리할 때 배치 간의 통계량이 불안정해지기 쉽기 때문입니다. Layer Normalization을 통해 Transformer는 배치 크기에 의존하지 않고 안정적인 학습이 가능해집니다.&lt;/p>
&lt;hr>
&lt;h1 id="8-디코더-특유의-구조-masked-attention과-cross-attention">8. 디코더 특유의 구조: Masked Attention과 Cross-Attention
&lt;/h1>&lt;p>지금까지 해설한 구조는 인코더의 것입니다. 문장을 생성하는 디코더 블록에서는 구조가 조금 다릅니다.&lt;/p>
&lt;h2 id="81-masked-multi-head-attention">8.1 Masked Multi-Head Attention
&lt;/h2>&lt;p>디코더의 역할은 &amp;lsquo;과거의 단어로부터 다음 단어를 예측하는 것&amp;rsquo;입니다. 따라서 훈련 시에 &amp;lsquo;미래의 단어&amp;rsquo;를 보게 되면 부정행위(커닝)가 되어 버립니다. 이를 방지하기 위한 수학적 조작이 **Masking(마스킹)**입니다.&lt;/p>
&lt;p>점수 행렬 $Q K^T$ 에 대해 상삼각 부분(미래의 정보에 해당)에 $-\infty$ 에 가까운 매우 작은 값을 설정하는 마스크 행렬 $M$ 을 더합니다.&lt;/p>
$$ M_{ij} = \begin{cases} 0 &amp; (i \le j) \\ -\infty &amp; (i > j) \end{cases} $$
$$ \text{Masked Attention}(Q, K, V) = \text{softmax}\left(\frac{Q K^T + M}{\sqrt{d_k}}\right) V $$
&lt;p>Softmax 함수를 계산할 때 $\exp(-\infty) = 0$ 이 되므로 미래 단어에 대한 Attention Weight는 완전히 $0$ 이 됩니다. 이를 통해 인과 관계(Causality)를 유지한 자기 회귀적(Autoregressive)인 생성이 가능해집니다.&lt;/p>
&lt;h2 id="82-encoder-decoder-cross-attention">8.2 Encoder-Decoder Cross-Attention
&lt;/h2>&lt;p>디코더의 두 번째 서브 레이어는 인코더의 출력을 참조하는 &lt;strong>Cross-Attention&lt;/strong>입니다.
여기서 $Q$ 는 직전 디코더 층에서 생성되지만, $K$ 와 $V$ 는 인코더의 최종 층 출력에서 생성됩니다.&lt;/p>
$$ Q_{decoder} = X_{dec} W^Q $$
$$ K_{encoder} = X_{enc} W^K $$
$$ V_{encoder} = X_{enc} W^V $$
&lt;p>이 계산을 통해 번역 작업 등에서 &amp;lsquo;현재 번역하고 있는 단어가 원래 외국어 문장의 어느 부분과 강하게 연관되어 있는지&amp;rsquo;를 모델이 학습할 수 있게 됩니다.&lt;/p>
&lt;hr>
&lt;h1 id="9-계산량과-현대의-최적화-수학">9. 계산량과 현대의 최적화 수학
&lt;/h1>&lt;p>Transformer는 훌륭한 모델이지만 그 수학적 구조로 인한 &amp;lsquo;약점&amp;rsquo;도 존재합니다.
Self-Attention의 계산량에 주목해 봅시다. 점수 행렬 $Q K^T$ 의 계산에서는 $(N \times d_k)$ 의 행렬과 $(d_k \times N)$ 의 행렬을 곱해야 하므로, 그 계산량은 **$O(N^2 \cdot d_{model})$**이 됩니다.&lt;/p>
&lt;p>즉, &lt;strong>시퀀스 길이 $N$ 에 대해 계산량과 메모리 사용량이 제곱으로 증가&lt;/strong>하는 것입니다.
문장이 짧은 경우에는 문제가 되지 않지만, 책 한 권 전체와 같은 방대한 컨텍스트를 LLM에 입력하려고 하면 $N$ 이 수만~수십만에 달해 기존의 Attention 계산에서는 GPU 메모리가 즉시 고갈되고 맙니다.&lt;/p>
&lt;p>이 $O(N^2)$ 의 저주를 끊어내기 위해 최근에는 수학적·하드웨어적 접근을 통한 다양한 최적화 기법이 제안되고 있습니다.
그 대표적인 예가 &lt;strong>FlashAttention&lt;/strong>입니다. FlashAttention은 GPU의 메모리 계층(SRAM과 HBM) 간의 데이터 전송(메모리 액세스)을 최소화하도록 Attention 계산을 타일 모양으로 분할(Tiling)하여 수행하는 알고리즘입니다. 수식상으로는 표준적인 Attention과 완전히 같은 결과를 출력(Exact Attention)함에도 불구하고, 하드웨어 수준의 최적화를 통해 극적인 속도 향상과 메모리 절감을 실현하였고 GPT-4와 같은 긴 문맥 모델의 구현을 가능하게 했습니다.&lt;/p>
&lt;p>그 밖에도 계산량을 $O(N \log N)$ 이나 $O(N)$ 으로 근사하는 Sparse Attention이나 Linear Attention 등의 연구도 활발히 진행되고 있습니다.&lt;/p>
&lt;hr>
&lt;h1 id="10-구현의-이미지pytorch-스타일의-의사-코드">10. 구현의 이미지(PyTorch 스타일의 의사 코드)
&lt;/h1>&lt;p>지금까지의 수학적 구조를 실제 프로그래밍 코드(Python / PyTorch)로 구현해 보면 놀라울 정도로 단순하게 작성할 수 있다는 것을 알 수 있습니다. Self-Attention의 핵심 부분에 대한 의사 코드(Pseudo-code)를 보여드립니다.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;span class="lnt">21
&lt;/span>&lt;span class="lnt">22
&lt;/span>&lt;span class="lnt">23
&lt;/span>&lt;span class="lnt">24
&lt;/span>&lt;span class="lnt">25
&lt;/span>&lt;span class="lnt">26
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="kn">import&lt;/span> &lt;span class="nn">torch&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">import&lt;/span> &lt;span class="nn">torch.nn.functional&lt;/span> &lt;span class="k">as&lt;/span> &lt;span class="nn">F&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">import&lt;/span> &lt;span class="nn">math&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">def&lt;/span> &lt;span class="nf">scaled_dot_product_attention&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">q&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">k&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">v&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">mask&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="kc">None&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># q, k, v의 형태: [batch_size, num_heads, seq_length, d_k]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">d_k&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">q&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">size&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># 1. 내적을 통한 점수 계산: Q * K^T&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># 마지막 두 차원을 전치(transpose)하여 행렬곱(matmul) 계산&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">scores&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">torch&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">matmul&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">q&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">k&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">transpose&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">2&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="o">-&lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="p">))&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># 2. 스케일링&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">scores&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">scores&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="n">math&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">sqrt&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">d_k&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># 3. 마스킹 (Masked Attention의 경우)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="n">mask&lt;/span> &lt;span class="ow">is&lt;/span> &lt;span class="ow">not&lt;/span> &lt;span class="kc">None&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">scores&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">scores&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">masked_fill&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">mask&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="o">-&lt;/span>&lt;span class="mf">1e9&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># 4. Softmax를 통한 확률화&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">attention_weights&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">F&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">softmax&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">scores&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">dim&lt;/span>&lt;span class="o">=-&lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># 5. Value 행렬 곱하기&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">output&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">torch&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">matmul&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">attention_weights&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">v&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="n">output&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">attention_weights&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>수식으로 표현된 $Q K^T / \sqrt{d_k}$ 가 &lt;code>torch.matmul(q, k.transpose(-2, -1)) / math.sqrt(d_k)&lt;/code> 로 직관적으로 구현되어 있다는 것을 알 수 있습니다. 수학의 이론이 고도화된 최적화 라이브러리의 힘을 빌려 단 몇 줄의 코드로 구현된다는 것은 딥러닝의 매우 흥미로운 측면입니다.&lt;/p>
&lt;hr>
&lt;h1 id="맺음말-수식에서-보이는-지능의-형태">맺음말: 수식에서 보이는 &amp;lsquo;지능&amp;rsquo;의 형태
&lt;/h1>&lt;p>본 문서에서는 Transformer 모델의 심오한 곳에 있는 수학적 구조를 파헤쳐 보았습니다.&lt;/p>
&lt;p>단어를 다차원 벡터 공간에 매핑하는 Embedding, 위치 정보를 삼각파의 합성으로 표현하는 Positional Encoding, 그리고 정보 검색의 비유에서 비롯된 행렬의 내적 계산인 Self-Attention 메커니즘. 이들 하나하나의 컴포넌트는 선형대수학, 미적분학, 확률과 통계라는 기초적인 수학이 쌓여 이루어진 것에 불과합니다.&lt;/p>
&lt;p>하지만 이러한 단순한 행렬 연산이 겹겹이 쌓이고, 수십억 수천억 개의 파라미터를 통해 거대한 데이터 세트로부터 패턴을 학습할 때, 그곳에는 우리의 &amp;lsquo;언어&amp;rsquo;를 이해하고 논리적인 추론을 하며 때로는 창의적인 아이디어를 만들어 내는 것 같은 &amp;lsquo;지능의 형태&amp;rsquo;가 나타납니다.&lt;/p>
&lt;p>&amp;lsquo;Attention Is All You Need&amp;rsquo;라는 도발적인 제목이 보여주듯 복잡한 순환 처리나 합성곱 처리를 버리고 순수한 &amp;lsquo;어텐션(연관도)&amp;rsquo; 계산에 특화된 이 아키텍처의 아름다움은 바로 그 수학적인 단순함에 있다고 할 수 있을 것입니다.&lt;/p>
&lt;p>앞으로 Transformer를 뛰어넘는 새로운 아키텍처(State Space Model인 Mamba 등)가 등장할 가능성도 있지만, Transformer가 구축한 &amp;lsquo;Attention을 통한 문맥 이해&amp;rsquo;의 수학적 프레임워크는 AI의 역사에 영원히 새겨질 것입니다.&lt;/p>
&lt;p>만약 여러분이 앞으로 ChatGPT나 Claude 등의 LLM을 사용할 기회가 있다면, 그 백그라운드에서 초당 수조 번의 $Q K^T$ 행렬곱이 계산되고 Softmax 함수가 확률을 산출해 내는 모습을 상상해 보세요. 기술에 대한 해상도가 높아져 AI의 세계가 더욱 흥미롭게 느껴질 것입니다.&lt;/p>
&lt;h3 id="참고-문헌">참고 문헌
&lt;/h3>&lt;ul>
&lt;li>Vaswani, A., et al. (2017). &amp;ldquo;Attention Is All You Need.&amp;rdquo; &lt;em>Advances in Neural Information Processing Systems&lt;/em>.&lt;/li>
&lt;li>Alammar, J. (2018). &amp;ldquo;The Illustrated Transformer.&amp;rdquo;&lt;/li>
&lt;/ul>
&lt;hr>
&lt;p>&lt;em>이 글은 자연어 처리와 AI의 수학적 기초를 배우려는 분들을 위한 가이드로 작성되었습니다. 질문이나 토론할 내용이 있다면 꼭 댓글로 알려주세요!&lt;/em>&lt;/p></description></item><item><title>페르마의 소정리를 활용한 암호화의 기초와 구현</title><link>http://kenji.blog/ko/p/fermats-little-theorem-cryptography-implementation/</link><pubDate>Fri, 11 Sep 2026 22:00:00 +0900</pubDate><guid>http://kenji.blog/ko/p/fermats-little-theorem-cryptography-implementation/</guid><description>&lt;img src="http://kenji.blog/p/fermats-little-theorem-cryptography-implementation/img/eyecatch.jpg" alt="Featured image of post 페르마의 소정리를 활용한 암호화의 기초와 구현" />&lt;h2 id="1-시작하며-현대-암호를-지탱하는-수학의-신비">1. 시작하며: 현대 암호를 지탱하는 수학의 신비
&lt;/h2>&lt;p>현대 디지털 사회, 특히 인터넷을 통한 통신에서 &amp;lsquo;암호화&amp;rsquo;는 필수 불가결한 기반 기술이 되었습니다. 우리가 웹 브라우저에서 HTTPS를 통해 안전하게 웹사이트를 탐색하고, 온라인 뱅킹으로 금융 거래를 하며, 메시징 앱으로 사적인 대화를 나눌 수 있는 것은 고도의 수학적 이론이 뒷받침된 암호 프로토콜이 배후에서 작동하고 있기 때문입니다. 그중에서도 특히 중요한 역할을 담당하고 있는 것이 &amp;lsquo;공개키 암호 방식&amp;rsquo;이며, 그 대표적인 예가 &lt;strong>RSA 암호&lt;/strong>입니다.&lt;/p>
&lt;p>RSA 암호를 비롯한 많은 암호 알고리즘의 안전성과 정당성은 17세기 프랑스의 수학자 피에르 드 페르마(Pierre de Fermat)가 발견한 매우 아름답고 강력한 정리에 크게 의존하고 있습니다. 그것이 바로 **페르마의 소정리(Fermat&amp;rsquo;s Little Theorem)**입니다. 더 나아가 이를 일반화한 레온하르트 오일러(Leonhard Euler)의 정리 역시 암호 이론에서 결정적인 역할을 하고 있습니다.&lt;/p>
&lt;p>본 기사에서는 페르마의 소정리라는 순수 수학의 발견이 어떻게 현대의 실용적인 암호 기술, 특히 &amp;lsquo;소수 판별&amp;rsquo;과 &amp;lsquo;RSA 암호&amp;rsquo;에 응용되고 있는지를 기초부터 철저하게 해설합니다. 수학적인 증명, 암호화 및 복호화의 메커니즘, 그리고 C++와 Python을 활용한 구체적인 알고리즘 구현까지 모두 다루는 매우 상세한 기술 가이드가 될 것입니다.&lt;/p>
&lt;hr>
&lt;h2 id="2-합동식과-모듈러-연산의-기초">2. 합동식과 모듈러 연산의 기초
&lt;/h2>&lt;p>페르마의 소정리를 이해하기 위해서는 먼저 &amp;lsquo;모듈러 연산(합동식)&amp;lsquo;이라는 수학 개념에 친숙해질 필요가 있습니다. 모듈러 연산이란 어떤 정해진 수(법, 모듈러스라고 부름)로 나눈 &amp;lsquo;나머지&amp;rsquo;에 주목한 계산 체계를 말합니다. 시계의 문자판(12시간에 1바퀴를 돎)과 같은 계산이기 때문에 &amp;lsquo;시계 산술&amp;rsquo;이라고도 불립니다.&lt;/p>
&lt;p>정수 $a$와 $b$를 양의 정수 $n$으로 나눈 나머지가 같을 때, 수학적으로는 다음과 같이 기술합니다.&lt;/p>
$$
a \equiv b \pmod n
$$
&lt;p>이것은 &amp;lsquo;$n$을 법으로 하여 $a$와 $b$는 합동이다&amp;rsquo;라고 읽습니다. 예를 들어, 17을 5로 나눈 나머지는 2이고, 12를 5로 나눈 나머지 역시 2입니다. 따라서 다음과 같이 쓸 수 있습니다.&lt;/p>
$$
17 \equiv 12 \pmod 5 \equiv 2 \pmod 5
$$
&lt;p>모듈러 연산에서는 일반적인 사칙연산(덧셈, 뺄셈, 곱셈)이 그대로 성립합니다.&lt;/p>
&lt;ol>
&lt;li>&lt;strong>덧셈&lt;/strong>: $a \equiv b \pmod n$ 이고 $c \equiv d \pmod n$ 이면, $a + c \equiv b + d \pmod n$&lt;/li>
&lt;li>&lt;strong>뺄셈&lt;/strong>: $a \equiv b \pmod n$ 이고 $c \equiv d \pmod n$ 이면, $a - c \equiv b - d \pmod n$&lt;/li>
&lt;li>&lt;strong>곱셈&lt;/strong>: $a \equiv b \pmod n$ 이고 $c \equiv d \pmod n$ 이면, $a \times c \equiv b \times d \pmod n$&lt;/li>
&lt;li>&lt;strong>거듭제곱&lt;/strong>: $a \equiv b \pmod n$ 이면, 임의의 자연수 $k$에 대해 $a^k \equiv b^k \pmod n$&lt;/li>
&lt;/ol>
&lt;p>단, &lt;strong>나눗셈&lt;/strong>에 대해서는 주의가 필요합니다. 일반적으로 $a \times c \equiv b \times c \pmod n$ 이라고 해서 양변을 $c$로 나누어 $a \equiv b \pmod n$ 으로 만들 수는 없습니다. 이것이 성립하는 것은 $c$와 $n$이 서로소(최대공약수가 1)인 경우에 한합니다. 이 &amp;lsquo;모듈러 역원&amp;rsquo;의 개념은 후술할 RSA 암호의 키 생성에서 극히 중요해집니다.&lt;/p>
&lt;hr>
&lt;h2 id="3-페르마의-소정리의-수학적-배경과-증명">3. 페르마의 소정리의 수학적 배경과 증명
&lt;/h2>&lt;p>모듈러 연산의 기초를 다졌으니, 본 주제인 페르마의 소정리에 대해 살펴보겠습니다.&lt;/p>
&lt;h3 id="31-정리의-정의">3.1 정리의 정의
&lt;/h3>&lt;p>페르마의 소정리는 다음과 같이 정식화됩니다.&lt;/p>
&lt;blockquote>
&lt;p>&lt;strong>페르마의 소정리 (Fermat&amp;rsquo;s Little Theorem)&lt;/strong>
$p$를 소수라 하고, $a$를 $p$의 배수가 아닌(즉 $a$와 $p$는 서로소인) 임의의 정수라고 하자. 이때 다음의 합동식이 성립한다.
&lt;/p>
$$ a^{p-1} \equiv 1 \pmod p $$
&lt;/blockquote>
&lt;p>또한, 조건 &amp;lsquo;$a$가 $p$의 배수가 아닐 것&amp;rsquo;을 제외하고, 모든 정수 $a$에 대해 성립하는 형태로 표현하는 것도 일반적입니다. 그 경우는 양변에 $a$를 곱하여 다음과 같이 됩니다.&lt;/p>
$$
a^p \equiv a \pmod p
$$
&lt;h3 id="32-구체적인-예를-통한-확인">3.2 구체적인 예를 통한 확인
&lt;/h3>&lt;p>정리가 정말로 성립하는지 구체적인 숫자를 사용하여 확인해 봅시다.
소수 $p = 5$라고 합시다. $p-1 = 4$입니다. $a$로서 $p$의 배수가 아닌 정수를 선택합니다.&lt;/p>
&lt;ul>
&lt;li>$a = 2$인 경우: $2^{5-1} = 2^4 = 16$. $16 \div 5 = 3$ 나머지 $1$. 따라서 $16 \equiv 1 \pmod 5$. (성립)&lt;/li>
&lt;li>$a = 3$인 경우: $3^{5-1} = 3^4 = 81$. $81 \div 5 = 16$ 나머지 $1$. 따라서 $81 \equiv 1 \pmod 5$. (성립)&lt;/li>
&lt;li>$a = 4$인 경우: $4^{5-1} = 4^4 = 256$. $256 \div 5 = 51$ 나머지 $1$. 따라서 $256 \equiv 1 \pmod 5$. (성립)&lt;/li>
&lt;/ul>
&lt;p>이처럼 어떤 $a$를 선택하든(5의 배수만 아니라면) 4제곱하여 5로 나눈 나머지는 항상 1이 됩니다. 마법처럼 보이지만, 이는 소수가 가지는 아름다운 성질에서 유래한 것입니다.&lt;/p>
&lt;h3 id="33-정리의-수학적-증명">3.3 정리의 수학적 증명
&lt;/h3>&lt;p>어째서 이런 일이 성립하는 것일까요. 여기서는 잉여류의 집합을 이용한 우아한 증명을 소개합니다.&lt;/p>
&lt;p>집합 $S = \{1, 2, 3, \dots, p-1\}$을 생각합니다. 이들은 $p$로 나눈 나머지가 $1$부터 $p-1$이 되는 정수들의 대표원입니다.
여기서 각 원소에 $p$와 서로소인 정수 $a$를 곱한 새로운 집합 $T$를 생각합니다.
&lt;/p>
$$ T = \{1a, 2a, 3a, \dots, (p-1)a\} $$
&lt;p>이 집합 $T$의 각 원소를 $p$로 나눈 나머지를 생각해 봅니다. 놀랍게도 이 나머지들은 순서는 바뀔지 몰라도 원래 집합 $S$의 원소들의 집합과 완전히 일치합니다.
왜냐하면:&lt;/p>
&lt;ol>
&lt;li>$T$의 원소가 $p$의 배수가 되는 일은 없습니다($a$도 원래의 원소도 $p$의 배수가 아니기 때문).&lt;/li>
&lt;li>$T$ 안에서 $p$를 법으로 하여 합동이 되는 서로 다른 두 원소는 존재하지 않습니다. 만약 $ia \equiv ja \pmod p$ ($i \neq j$)라고 한다면, $a$와 $p$는 서로소이므로 $a$로 나눌 수 있고, $i \equiv j \pmod p$가 되어 모순되기 때문입니다.&lt;/li>
&lt;/ol>
&lt;p>따라서 $S$의 원소를 모두 곱한 것과 $T$의 원소를 모두 곱한 것은 $p$를 법으로 하여 합동이 됩니다.&lt;/p>
$$
(1a) \times (2a) \times \dots \times ((p-1)a) \equiv 1 \times 2 \times \dots \times (p-1) \pmod p
$$
&lt;p>좌변을 정리하면 $a$가 $p-1$개 있으므로,&lt;/p>
$$
a^{p-1} \cdot (p-1)! \equiv (p-1)! \pmod p
$$
&lt;p>$(p-1)!$은 $p$와 서로소이므로 양변을 $(p-1)!$로 나눌 수 있으며, 최종적으로 다음의 정리가 도출됩니다.&lt;/p>
$$
a^{p-1} \equiv 1 \pmod p
$$
&lt;p>이것이 페르마의 소정리의 증명입니다.&lt;/p>
&lt;hr>
&lt;h2 id="4-오일러의-피-함수와-오일러의-정리">4. 오일러의 피 함수와 오일러의 정리
&lt;/h2>&lt;p>페르마의 소정리는 &amp;lsquo;소수 $p$&amp;lsquo;에 관한 정리지만, 이를 &amp;lsquo;임의의 양의 정수 $n$&amp;lsquo;으로 일반화한 사람이 레온하르트 오일러입니다. RSA 암호를 이해하려면 이 확장이 필수적입니다.&lt;/p>
&lt;h3 id="41-오일러의-피토션트-함수-phin">4.1 오일러의 피(토션트) 함수 $\phi(n)$
&lt;/h3>&lt;p>오일러의 피 함수(또는 오일러의 $\phi$ 함수) $\phi(n)$은 &amp;lsquo;$1$부터 $n$까지의 정수 중 $n$과 서로소인 것의 개수&amp;rsquo;를 나타내는 함수입니다.&lt;/p>
&lt;ul>
&lt;li>소수 $p$의 경우, $1$부터 $p-1$까지의 모든 정수가 $p$와 서로소이므로 $\phi(p) = p - 1$이 됩니다.&lt;/li>
&lt;li>서로 다른 두 소수 $p, q$에 대해, 그 곱인 $n = p \times q$의 경우 $\phi(n)$은 매우 간단한 식으로 구해집니다.
$$ \phi(p \times q) = \phi(p) \times \phi(q) = (p - 1)(q - 1) $$&lt;/li>
&lt;/ul>
&lt;p>이 성질이 RSA 암호의 키 생성에 있어 근간이 되는 로직이 됩니다.&lt;/p>
&lt;h3 id="42-오일러의-정리">4.2 오일러의 정리
&lt;/h3>&lt;p>오일러는 페르마의 소정리를 다음과 같이 일반화했습니다.&lt;/p>
&lt;blockquote>
&lt;p>&lt;strong>오일러의 정리 (Euler&amp;rsquo;s Theorem)&lt;/strong>
양의 정수 $n$과, 그 수와 서로소인 정수 $a$에 대하여 다음이 성립한다.
&lt;/p>
$$ a^{\phi(n)} \equiv 1 \pmod n $$
&lt;/blockquote>
&lt;p>만약 $n$이 소수 $p$라면 $\phi(p) = p - 1$이므로, 이것은 페르마의 소정리 자체($a^{p-1} \equiv 1 \pmod p$)가 됩니다. 즉, 페르마의 소정리는 오일러의 정리의 특수한 경우에 불과합니다.&lt;/p>
&lt;hr>
&lt;h2 id="5-거대한-소수-찾기-페르마의-소수-판별법">5. 거대한 소수 찾기: 페르마의 소수 판별법
&lt;/h2>&lt;p>암호 기술(RSA 암호나 Diffie-Hellman 키 교환 등)에서는 수백 자리에 달하는 &amp;lsquo;거대한 소수&amp;rsquo;를 고속으로 찾아낼 필요가 있습니다. 하지만 거대한 수 $N$이 소수인지 판별하기 위해 $2$부터 $\sqrt{N}$까지의 모든 수로 나누어 떨어지는지 시험하는 &amp;lsquo;시도 나눗셈법&amp;rsquo;으로는 우주의 수명만큼의 시간이 걸려 버립니다.&lt;/p>
&lt;p>그래서 등장하는 것이 페르마의 소정리를 역으로 이용한 &amp;lsquo;확률적 소수 판별법&amp;rsquo;인 **페르마 테스트(Fermat Primality Test)**입니다.&lt;/p>
&lt;h3 id="51-확률적-소수-판별법이란">5.1 확률적 소수 판별법이란
&lt;/h3>&lt;p>페르마의 소정리에 의하면, $p$가 소수라면 임의의 $a$ ($1 &lt; a &lt; p$)에 대해 $a^{p-1} \equiv 1 \pmod p$ 가 반드시 성립합니다.
이의 대우를 취하면, &amp;lsquo;어떤 $a$에 대해 $a^{p-1} \not\equiv 1 \pmod p$ 가 될 경우, $p$는 &lt;strong>절대로 소수가 아니다(합성수이다)&lt;/strong>&amp;lsquo;라는 것을 말할 수 있습니다.&lt;/p>
&lt;p>따라서 $N$이 소수인지 판별하고 싶은 경우, 무작위로 몇 개의 $a$를 선택하여 $a^{N-1} \pmod N$을 계산해서 $1$이 되는지를 확인합니다. 만약 한 번이라도 $1$이 아닌 답이 나오면 $N$은 합성수임이 확정됩니다. 몇 번을 시험해도 $1$이 되는 경우 $N$은 &amp;lsquo;아마도 소수일 것이다&amp;rsquo;라고 높은 확률로 판단할 수 있습니다.&lt;/p>
&lt;h3 id="52-알고리즘의-해설과-플로우차트">5.2 알고리즘의 해설과 플로우차트
&lt;/h3>&lt;p>페르마 테스트의 알고리즘은 다음과 같습니다.&lt;/p>
&lt;div class="mermaid">flowchart TD
Start["시작"] --> Input["판별 대상의 수 p 와, 테스트 횟수 k 를 입력"]
Input --> LoopStart["i = 0 부터 k-1 까지 루프"]
LoopStart --> Condition{"i &lt; k ?"}
Condition -- "예" --> RandomA["1 &lt; a &lt; p-1 의 범위에서 무작위 정수 a 를 선택"]
RandomA --> Calc["모듈러 거듭제곱 a^(p-1) mod p 를 계산"]
Calc --> CheckPrime{"결과는 1 인가?"}
CheckPrime -- "아니요" --> ReturnComposite["p 는 합성수이다 (확정)"]
CheckPrime -- "예" --> Increment["i 를 증가시킴"]
Increment --> Condition
Condition -- "아니요" --> ReturnPrime["p 는 아마도 소수일 것이다 (확률적)"]
ReturnComposite --> End["종료"]
ReturnPrime --> End&lt;/div>
&lt;h3 id="53-카마이클-수위소수의-함정">5.3 카마이클 수(위소수)의 함정
&lt;/h3>&lt;p>페르마 테스트는 매우 빠르지만 중대한 단점이 있습니다. 그것은 합성수임에도 불구하고 모든 $a$에 대해 $a^{N-1} \equiv 1 \pmod N$을 만족시켜 버리는 악마와 같은 수가 존재한다는 것입니다. 이를 **카마이클 수(Carmichael numbers)**라고 부릅니다. 가장 작은 카마이클 수는 $561$ ($3 \times 11 \times 17$)입니다.&lt;/p>
&lt;p>카마이클 수가 존재하기 때문에 순수한 페르마 테스트만으로는 절대적인 소수 판별을 할 수 없습니다. 그러므로 실제 암호화 시스템(OpenSSL 등)에서는 페르마 테스트를 개량한 &lt;strong>밀러-라빈(Miller-Rabin) 소수 판별법&lt;/strong>이 표준적으로 사용되고 있습니다. 밀러-라빈 판별법은 카마이클 수를 꿰뚫어 볼 수 있어 오판 확률을 실질적으로 제로에 가깝게 만들 수 있습니다.&lt;/p>
&lt;h3 id="54-고속-거듭제곱-나머지-연산분할-정복-거듭제곱">5.4 고속 거듭제곱 나머지 연산(분할 정복 거듭제곱)
&lt;/h3>&lt;p>소수 판별 알고리즘 내에서 $a^{N-1} \pmod N$을 계산해야 하지만, $N$이 거대한 경우 $a^{N-1}$은 천문학적인 자릿수가 되어 컴퓨터 메모리에 다 들어가지 않습니다.
이를 해결하는 것이 &lt;strong>분할 정복 거듭제곱(Exponentiation by Squaring)&lt;/strong> 또는 모듈러 거듭제곱 연산입니다. 계산의 각 단계마다 나머지(mod N)를 취함으로써 항상 값을 $N$보다 작게 유지하며 매우 고속($O(\log N)$의 시간 복잡도)으로 계산 가능하게 됩니다.&lt;/p>
&lt;hr>
&lt;h2 id="6-소수-판별과-거듭제곱-나머지-구현">6. 소수 판별과 거듭제곱 나머지 구현
&lt;/h2>&lt;p>그러면 페르마의 소수 판별법과 분할 정복 거듭제곱을 C++와 Python으로 구현해 보겠습니다.&lt;/p>
&lt;h3 id="61-c를-이용한-구현">6.1 C++를 이용한 구현
&lt;/h3>&lt;p>C++에서는 표준 정수형이 오버플로우되기 쉽기 때문에 거대한 수를 다루려면 다정밀도 정수 라이브러리(GMP 등)가 필요하지만, 여기서는 알고리즘을 이해하기 위해 64비트 정수(&lt;code>unsigned long long&lt;/code>) 범위 내에서의 구현을 보여줍니다.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;span class="lnt">21
&lt;/span>&lt;span class="lnt">22
&lt;/span>&lt;span class="lnt">23
&lt;/span>&lt;span class="lnt">24
&lt;/span>&lt;span class="lnt">25
&lt;/span>&lt;span class="lnt">26
&lt;/span>&lt;span class="lnt">27
&lt;/span>&lt;span class="lnt">28
&lt;/span>&lt;span class="lnt">29
&lt;/span>&lt;span class="lnt">30
&lt;/span>&lt;span class="lnt">31
&lt;/span>&lt;span class="lnt">32
&lt;/span>&lt;span class="lnt">33
&lt;/span>&lt;span class="lnt">34
&lt;/span>&lt;span class="lnt">35
&lt;/span>&lt;span class="lnt">36
&lt;/span>&lt;span class="lnt">37
&lt;/span>&lt;span class="lnt">38
&lt;/span>&lt;span class="lnt">39
&lt;/span>&lt;span class="lnt">40
&lt;/span>&lt;span class="lnt">41
&lt;/span>&lt;span class="lnt">42
&lt;/span>&lt;span class="lnt">43
&lt;/span>&lt;span class="lnt">44
&lt;/span>&lt;span class="lnt">45
&lt;/span>&lt;span class="lnt">46
&lt;/span>&lt;span class="lnt">47
&lt;/span>&lt;span class="lnt">48
&lt;/span>&lt;span class="lnt">49
&lt;/span>&lt;span class="lnt">50
&lt;/span>&lt;span class="lnt">51
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-cpp" data-lang="cpp">&lt;span class="line">&lt;span class="cl">&lt;span class="cp">#include&lt;/span> &lt;span class="cpf">&amp;lt;iostream&amp;gt;&lt;/span>&lt;span class="cp">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp">#include&lt;/span> &lt;span class="cpf">&amp;lt;random&amp;gt;&lt;/span>&lt;span class="cp">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp">&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">using&lt;/span> &lt;span class="k">namespace&lt;/span> &lt;span class="n">std&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// 고속 거듭제곱 나머지 연산 (a^b mod m) - 분할 정복 거듭제곱
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="kt">unsigned&lt;/span> &lt;span class="kt">long&lt;/span> &lt;span class="kt">long&lt;/span> &lt;span class="nf">power_mod&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="kt">unsigned&lt;/span> &lt;span class="kt">long&lt;/span> &lt;span class="kt">long&lt;/span> &lt;span class="n">a&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="kt">unsigned&lt;/span> &lt;span class="kt">long&lt;/span> &lt;span class="kt">long&lt;/span> &lt;span class="n">b&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="kt">unsigned&lt;/span> &lt;span class="kt">long&lt;/span> &lt;span class="kt">long&lt;/span> &lt;span class="n">m&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kt">unsigned&lt;/span> &lt;span class="kt">long&lt;/span> &lt;span class="kt">long&lt;/span> &lt;span class="n">result&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">a&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">a&lt;/span> &lt;span class="o">%&lt;/span> &lt;span class="n">m&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">while&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">b&lt;/span> &lt;span class="o">&amp;gt;&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// b의 최하위 비트가 1인 경우, 결과에 a를 곱함
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">b&lt;/span> &lt;span class="o">%&lt;/span> &lt;span class="mi">2&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">result&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">__int128&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="n">result&lt;/span> &lt;span class="o">*&lt;/span> &lt;span class="n">a&lt;/span> &lt;span class="o">%&lt;/span> &lt;span class="n">m&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="c1">// 오버플로우 방지를 위해 128비트 확장
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// a를 제곱함
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="n">a&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">__int128&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="n">a&lt;/span> &lt;span class="o">*&lt;/span> &lt;span class="n">a&lt;/span> &lt;span class="o">%&lt;/span> &lt;span class="n">m&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// b를 오른쪽 시프트 (절반으로 만듦)
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="n">b&lt;/span> &lt;span class="o">/=&lt;/span> &lt;span class="mi">2&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="n">result&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// 페르마의 소수 판별법
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="kt">bool&lt;/span> &lt;span class="nf">fermat_is_prime&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="kt">unsigned&lt;/span> &lt;span class="kt">long&lt;/span> &lt;span class="kt">long&lt;/span> &lt;span class="n">p&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="kt">int&lt;/span> &lt;span class="n">iterations&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">5&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">p&lt;/span> &lt;span class="o">&amp;lt;=&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="k">return&lt;/span> &lt;span class="nb">false&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">p&lt;/span> &lt;span class="o">&amp;lt;=&lt;/span> &lt;span class="mi">3&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="k">return&lt;/span> &lt;span class="nb">true&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">p&lt;/span> &lt;span class="o">%&lt;/span> &lt;span class="mi">2&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="k">return&lt;/span> &lt;span class="nb">false&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">random_device&lt;/span> &lt;span class="n">rd&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">mt19937_64&lt;/span> &lt;span class="n">gen&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">rd&lt;/span>&lt;span class="p">());&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">uniform_int_distribution&lt;/span>&lt;span class="o">&amp;lt;&lt;/span>&lt;span class="kt">unsigned&lt;/span> &lt;span class="kt">long&lt;/span> &lt;span class="kt">long&lt;/span>&lt;span class="o">&amp;gt;&lt;/span> &lt;span class="n">dis&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">2&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">p&lt;/span> &lt;span class="o">-&lt;/span> &lt;span class="mi">2&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">for&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="kt">int&lt;/span> &lt;span class="n">i&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="n">i&lt;/span> &lt;span class="o">&amp;lt;&lt;/span> &lt;span class="n">iterations&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="o">++&lt;/span>&lt;span class="n">i&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kt">unsigned&lt;/span> &lt;span class="kt">long&lt;/span> &lt;span class="kt">long&lt;/span> &lt;span class="n">a&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">dis&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">gen&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// a^(p-1) mod p 가 1 이 아니면 합성수
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">power_mod&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">a&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">p&lt;/span> &lt;span class="o">-&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">p&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">!=&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="nb">false&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="nb">true&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="c1">// 아마도 소수
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kt">int&lt;/span> &lt;span class="nf">main&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kt">unsigned&lt;/span> &lt;span class="kt">long&lt;/span> &lt;span class="kt">long&lt;/span> &lt;span class="n">num&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">1000000007&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="c1">// 이미 알려진 소수
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">fermat_is_prime&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">num&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">10&lt;/span>&lt;span class="p">))&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">cout&lt;/span> &lt;span class="o">&amp;lt;&amp;lt;&lt;/span> &lt;span class="n">num&lt;/span> &lt;span class="o">&amp;lt;&amp;lt;&lt;/span> &lt;span class="s">&amp;#34; is probably prime.&amp;#34;&lt;/span> &lt;span class="o">&amp;lt;&amp;lt;&lt;/span> &lt;span class="n">endl&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span> &lt;span class="k">else&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">cout&lt;/span> &lt;span class="o">&amp;lt;&amp;lt;&lt;/span> &lt;span class="n">num&lt;/span> &lt;span class="o">&amp;lt;&amp;lt;&lt;/span> &lt;span class="s">&amp;#34; is composite.&amp;#34;&lt;/span> &lt;span class="o">&amp;lt;&amp;lt;&lt;/span> &lt;span class="n">endl&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h3 id="62-python을-이용한-구현">6.2 Python을 이용한 구현
&lt;/h3>&lt;p>Python의 표준 정수형은 다정밀도 정수를 지원하므로 자릿수 넘침을 걱정할 필요가 없습니다. 게다가 Python의 내장 함수 &lt;code>pow(a, b, m)&lt;/code>는 내부적으로 분할 정복 거듭제곱을 사용하기 때문에 매우 빠릅니다.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;span class="lnt">21
&lt;/span>&lt;span class="lnt">22
&lt;/span>&lt;span class="lnt">23
&lt;/span>&lt;span class="lnt">24
&lt;/span>&lt;span class="lnt">25
&lt;/span>&lt;span class="lnt">26
&lt;/span>&lt;span class="lnt">27
&lt;/span>&lt;span class="lnt">28
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="kn">import&lt;/span> &lt;span class="nn">random&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">def&lt;/span> &lt;span class="nf">fermat_is_prime&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">p&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">iterations&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">5&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;&amp;#34;&amp;#34;
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2"> 페르마의 소수 판별법을 이용한 확률적 소수 판별
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2"> &amp;#34;&amp;#34;&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="n">p&lt;/span> &lt;span class="o">&amp;lt;=&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="kc">False&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="n">p&lt;/span> &lt;span class="o">&amp;lt;=&lt;/span> &lt;span class="mi">3&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="kc">True&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="n">p&lt;/span> &lt;span class="o">%&lt;/span> &lt;span class="mi">2&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="kc">False&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">for&lt;/span> &lt;span class="n">_&lt;/span> &lt;span class="ow">in&lt;/span> &lt;span class="nb">range&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">iterations&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># 2 에서 p-2 사이의 무작위 수 a 를 고름&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">a&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">random&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">randint&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">2&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">p&lt;/span> &lt;span class="o">-&lt;/span> &lt;span class="mi">2&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># a^(p-1) mod p 를 계산. 내장 pow 는 고속.&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="nb">pow&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">a&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">p&lt;/span> &lt;span class="o">-&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">p&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">!=&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="kc">False&lt;/span> &lt;span class="c1"># 합성수 확정&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="kc">True&lt;/span> &lt;span class="c1"># 아마도 소수&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 테스트&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">number_to_test&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">104729&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">if&lt;/span> &lt;span class="n">fermat_is_prime&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">number_to_test&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">10&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nb">print&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="sa">f&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">number_to_test&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2"> 는 아마도 소수입니다.&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">else&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nb">print&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="sa">f&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">number_to_test&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2"> 는 합성수입니다.&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;hr>
&lt;h2 id="7-rsa-암호에의-응용-페르마와-오일러가-결실을-맺는-곳">7. RSA 암호에의 응용: 페르마와 오일러가 결실을 맺는 곳
&lt;/h2>&lt;p>페르마의 소정리(및 오일러의 정리)의 가장 위대한 응용처가 1977년에 Rivest, Shamir, Adleman 세 사람에 의해 개발된 &lt;strong>RSA 암호&lt;/strong>입니다.
RSA 암호는 &amp;lsquo;공개키 암호&amp;rsquo;라는 획기적인 시스템으로, 암호화하기 위한 키(공개키)는 전 세계에 공개해 두면서, 복호화하기 위한 키(비밀키)는 수신자 본인만이 알고 있는 구조를 구현하고 있습니다.&lt;/p>
&lt;p>이 비대칭성은 &amp;lsquo;거대한 합성수의 소인수분해는 지극히 어렵다&amp;rsquo;라는 계산 복잡도 기반 안전성에 근거하고 있습니다.&lt;/p>
&lt;h3 id="71-rsa-암호의-구조-키-생성-암호화-복호화">7.1 RSA 암호의 구조 (키 생성, 암호화, 복호화)
&lt;/h3>&lt;p>RSA 암호의 전체적인 통신 흐름을 Mermaid 시퀀스 다이어그램으로 확인해 봅시다.&lt;/p>
&lt;div class="mermaid">sequenceDiagram
participant Alice["앨리스 (수신자)"]
participant Bob["밥 (송신자)"]
Alice->>Alice: "거대한 소수 p, q 를 생성"
Alice->>Alice: "N = p * q, φ(N) = (p-1)(q-1) 을 계산"
Alice->>Alice: "공개키 e 와 비밀키 d 를 계산 (e*d ≡ 1 mod φ(N))"
Alice->>Bob: "공개키 (N, e) 를 전송"
Note over Bob: "평문 M 을 준비 (M &lt; N)"
Bob->>Bob: "암호문 C = M^e mod N 을 계산"
Bob->>Alice: "암호문 C 를 전송"
Alice->>Alice: "평문 M = C^d mod N 을 계산하여 복호화"&lt;/div>
&lt;p>아래에 수학적인 세부 단계를 해설합니다.&lt;/p>
&lt;h4 id="단계-1-키-생성-수신자-앨리스의-작업">단계 1: 키 생성 (수신자 앨리스의 작업)
&lt;/h4>&lt;ol>
&lt;li>두 개의 거대한 소수 $p$와 $q$를 무작위로 생성합니다(여기서 앞서 말한 소수 판별법이 사용됩니다).&lt;/li>
&lt;li>그 곱인 $N = p \times q$를 계산합니다. 이 $N$은 공개됩니다.&lt;/li>
&lt;li>오일러의 피 함수를 사용하여 $\phi(N) = (p-1)(q-1)$을 계산합니다.&lt;/li>
&lt;li>$\phi(N)$과 서로소인 정수 $e$(공개 지수)를 선택합니다(종종 $e = 65537$이 사용됩니다).&lt;/li>
&lt;li>$e$의 모듈러 역원 $d$(비밀 지수)를 계산합니다. 즉, 다음을 만족하는 $d$를 찾습니다.
$$ e \cdot d \equiv 1 \pmod{\phi(N)} $$
이 계산에는 &lt;strong>확장 유클리드 호제법&lt;/strong>이 사용됩니다.&lt;/li>
&lt;/ol>
&lt;p>이것으로 &lt;strong>공개키는 $(N, e)$&lt;/strong>, **비밀키는 $(N, d)$**가 됩니다. ($p, q, \phi(N)$은 즉시 파기하거나 엄중히 숨깁니다).&lt;/p>
&lt;h4 id="단계-2-암호화-송신자-밥의-작업">단계 2: 암호화 (송신자 밥의 작업)
&lt;/h4>&lt;p>밥은 앨리스에게 메시지 $M$을 보내고 싶다고 합시다($M$은 문자를 수치화한 것으로 $0 \le M &lt; N$입니다).
밥은 앨리스의 공개키 $(N, e)$를 사용하여 다음 계산을 수행해 암호문 $C$를 만듭니다.&lt;/p>
$$
C \equiv M^e \pmod N
$$
&lt;p>이 $C$를 네트워크를 통해 앨리스에게 송신합니다.&lt;/p>
&lt;h4 id="단계-3-복호화-수신자-앨리스의-작업">단계 3: 복호화 (수신자 앨리스의 작업)
&lt;/h4>&lt;p>암호문 $C$를 받은 앨리스는 자신만이 알고 있는 비밀키 $d$를 사용하여 다음 계산을 수행합니다.&lt;/p>
$$
M' \equiv C^d \pmod N
$$
&lt;p>놀랍게도 이 계산 결과 $M'$은 원래의 메시지 $M$과 완전히 일치합니다.&lt;/p>
&lt;h3 id="72-왜-복호화가-가능한가-수학적-증명">7.2 왜 복호화가 가능한가? (수학적 증명)
&lt;/h3>&lt;p>여기서 페르마의 소정리(오일러의 정리)가 진가를 발휘합니다. 어째서 $C^d \pmod N$이 $M$으로 되돌아가는 것일까요?&lt;/p>
&lt;p>복호화 식을 전개해 봅니다.
$C \equiv M^e \pmod N$ 이므로,
&lt;/p>
$$ C^d \equiv (M^e)^d \equiv M^{ed} \pmod N $$
&lt;p>키 생성 단계에서 $e \cdot d \equiv 1 \pmod{\phi(N)}$ 이 되도록 $d$를 선택했습니다. 이것은 어떤 정수 $k$가 존재하여 다음과 같이 쓸 수 있음을 의미합니다.
&lt;/p>
$$ e \cdot d = 1 + k \cdot \phi(N) $$
&lt;p>이것을 위의 식에 대입합니다.
&lt;/p>
$$ M^{ed} = M^{1 + k \cdot \phi(N)} = M \cdot M^{k \cdot \phi(N)} = M \cdot (M^{\phi(N)})^k \pmod N $$
&lt;p>여기서 &lt;strong>오일러의 정리&lt;/strong> ($M^{\phi(N)} \equiv 1 \pmod N$)가 등장합니다. (※엄밀히는 $M$과 $N$이 서로소여야 하지만, RSA에서는 $M$과 $N$이 서로소가 아닐 확률이 천문학적으로 낮으며 중국인의 나머지 정리를 사용하면 서로소가 아니어도 성립함이 증명 가능합니다).&lt;/p>
&lt;p>오일러의 정리를 적용하면 $M^{\phi(N)} \equiv 1$ 이므로,
&lt;/p>
$$ M \cdot (1)^k \equiv M \pmod N $$
&lt;p>보기 좋게 $M$이 복원되었습니다! 페르마와 오일러가 수백 년 전에 발견한 수의 성질이 현대 디지털 통신의 기밀성을 완벽하게 보장하고 있는 것입니다.&lt;/p>
&lt;hr>
&lt;h2 id="8-rsa-암호의-토이-구현-python">8. RSA 암호의 토이 구현 (Python)
&lt;/h2>&lt;p>이론만으로는 실감이 나지 않기 때문에 Python을 사용하여 실제로 RSA 암호의 키 생성, 암호화, 복호화의 과정을 구현해 보겠습니다. 이것은 교육용 &amp;lsquo;토이(장난감) 구현&amp;rsquo;이지만 사용된 수학은 진짜와 완전히 같습니다.&lt;/p>
&lt;p>모듈러 역원 $d$를 구하기 위한 &amp;lsquo;확장 유클리드 호제법&amp;rsquo;도 구현에 포함시킵니다.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;span class="lnt">21
&lt;/span>&lt;span class="lnt">22
&lt;/span>&lt;span class="lnt">23
&lt;/span>&lt;span class="lnt">24
&lt;/span>&lt;span class="lnt">25
&lt;/span>&lt;span class="lnt">26
&lt;/span>&lt;span class="lnt">27
&lt;/span>&lt;span class="lnt">28
&lt;/span>&lt;span class="lnt">29
&lt;/span>&lt;span class="lnt">30
&lt;/span>&lt;span class="lnt">31
&lt;/span>&lt;span class="lnt">32
&lt;/span>&lt;span class="lnt">33
&lt;/span>&lt;span class="lnt">34
&lt;/span>&lt;span class="lnt">35
&lt;/span>&lt;span class="lnt">36
&lt;/span>&lt;span class="lnt">37
&lt;/span>&lt;span class="lnt">38
&lt;/span>&lt;span class="lnt">39
&lt;/span>&lt;span class="lnt">40
&lt;/span>&lt;span class="lnt">41
&lt;/span>&lt;span class="lnt">42
&lt;/span>&lt;span class="lnt">43
&lt;/span>&lt;span class="lnt">44
&lt;/span>&lt;span class="lnt">45
&lt;/span>&lt;span class="lnt">46
&lt;/span>&lt;span class="lnt">47
&lt;/span>&lt;span class="lnt">48
&lt;/span>&lt;span class="lnt">49
&lt;/span>&lt;span class="lnt">50
&lt;/span>&lt;span class="lnt">51
&lt;/span>&lt;span class="lnt">52
&lt;/span>&lt;span class="lnt">53
&lt;/span>&lt;span class="lnt">54
&lt;/span>&lt;span class="lnt">55
&lt;/span>&lt;span class="lnt">56
&lt;/span>&lt;span class="lnt">57
&lt;/span>&lt;span class="lnt">58
&lt;/span>&lt;span class="lnt">59
&lt;/span>&lt;span class="lnt">60
&lt;/span>&lt;span class="lnt">61
&lt;/span>&lt;span class="lnt">62
&lt;/span>&lt;span class="lnt">63
&lt;/span>&lt;span class="lnt">64
&lt;/span>&lt;span class="lnt">65
&lt;/span>&lt;span class="lnt">66
&lt;/span>&lt;span class="lnt">67
&lt;/span>&lt;span class="lnt">68
&lt;/span>&lt;span class="lnt">69
&lt;/span>&lt;span class="lnt">70
&lt;/span>&lt;span class="lnt">71
&lt;/span>&lt;span class="lnt">72
&lt;/span>&lt;span class="lnt">73
&lt;/span>&lt;span class="lnt">74
&lt;/span>&lt;span class="lnt">75
&lt;/span>&lt;span class="lnt">76
&lt;/span>&lt;span class="lnt">77
&lt;/span>&lt;span class="lnt">78
&lt;/span>&lt;span class="lnt">79
&lt;/span>&lt;span class="lnt">80
&lt;/span>&lt;span class="lnt">81
&lt;/span>&lt;span class="lnt">82
&lt;/span>&lt;span class="lnt">83
&lt;/span>&lt;span class="lnt">84
&lt;/span>&lt;span class="lnt">85
&lt;/span>&lt;span class="lnt">86
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="kn">import&lt;/span> &lt;span class="nn">random&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 최대공약수를 구함&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">def&lt;/span> &lt;span class="nf">gcd&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">a&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">b&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">while&lt;/span> &lt;span class="n">b&lt;/span> &lt;span class="o">!=&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">a&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">b&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">b&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">a&lt;/span> &lt;span class="o">%&lt;/span> &lt;span class="n">b&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="n">a&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 확장 유클리드 호제법 (ax + by = gcd(a,b) 의 x, y 를 구함)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># e*d ≡ 1 (mod φ(N)) 의 d 를 찾기 위해 사용&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">def&lt;/span> &lt;span class="nf">extended_gcd&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">a&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">b&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="n">a&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">b&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">else&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">g&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">y&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">x&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">extended_gcd&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">b&lt;/span> &lt;span class="o">%&lt;/span> &lt;span class="n">a&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">a&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">g&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">x&lt;/span> &lt;span class="o">-&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">b&lt;/span> &lt;span class="o">//&lt;/span> &lt;span class="n">a&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">*&lt;/span> &lt;span class="n">y&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">y&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">def&lt;/span> &lt;span class="nf">mod_inverse&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">e&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">phi&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">g&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">x&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">y&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">extended_gcd&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">e&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">phi&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="n">g&lt;/span> &lt;span class="o">!=&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">raise&lt;/span> &lt;span class="ne">Exception&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;역원이 존재하지 않습니다&amp;#39;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">else&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="n">x&lt;/span> &lt;span class="o">%&lt;/span> &lt;span class="n">phi&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 소수 생성 함수 (간이판: 작은 소수를 생성)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">def&lt;/span> &lt;span class="nf">generate_prime&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">bits&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">while&lt;/span> &lt;span class="kc">True&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">p&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">random&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">getrandbits&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">bits&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># 앞서 말한 페르마 테스트 대신 간이로 판별&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="n">p&lt;/span> &lt;span class="o">&amp;gt;&lt;/span> &lt;span class="mi">1&lt;/span> &lt;span class="ow">and&lt;/span> &lt;span class="nb">pow&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">2&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">p&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">p&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="mi">1&lt;/span> &lt;span class="ow">and&lt;/span> &lt;span class="nb">pow&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">3&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">p&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">p&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="n">p&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># RSA 키 생성&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">def&lt;/span> &lt;span class="nf">generate_keypair&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">bits&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">16&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">p&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">generate_prime&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">bits&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">q&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">generate_prime&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">bits&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># p 와 q 가 같아지지 않도록 함&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">while&lt;/span> &lt;span class="n">p&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="n">q&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">q&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">generate_prime&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">bits&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">n&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">p&lt;/span> &lt;span class="o">*&lt;/span> &lt;span class="n">q&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">phi&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">p&lt;/span> &lt;span class="o">-&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">*&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">q&lt;/span> &lt;span class="o">-&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># e 는 65537 등의 소수가 자주 사용되지만, 여기서는 무작위로 고름&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">e&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">random&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">randrange&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">phi&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">g&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">gcd&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">e&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">phi&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">while&lt;/span> &lt;span class="n">g&lt;/span> &lt;span class="o">!=&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">e&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">random&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">randrange&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">phi&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">g&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">gcd&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">e&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">phi&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># 비밀키 d 계산&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">d&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">mod_inverse&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">e&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">phi&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># 공개키 (e, n), 비밀키 (d, n)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="p">((&lt;/span>&lt;span class="n">e&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">n&lt;/span>&lt;span class="p">),&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">d&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">n&lt;/span>&lt;span class="p">))&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">def&lt;/span> &lt;span class="nf">encrypt&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">pk&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">plaintext&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">e&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">n&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">pk&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># plaintext^e mod n 을 계산&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">cipher&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="nb">pow&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nb">ord&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">char&lt;/span>&lt;span class="p">),&lt;/span> &lt;span class="n">e&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">n&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="k">for&lt;/span> &lt;span class="n">char&lt;/span> &lt;span class="ow">in&lt;/span> &lt;span class="n">plaintext&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="n">cipher&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">def&lt;/span> &lt;span class="nf">decrypt&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">sk&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">ciphertext&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">d&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">n&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">sk&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># cipher^d mod n 을 계산하고 문자로 되돌림&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">plain&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="nb">chr&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nb">pow&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">char&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">d&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">n&lt;/span>&lt;span class="p">))&lt;/span> &lt;span class="k">for&lt;/span> &lt;span class="n">char&lt;/span> &lt;span class="ow">in&lt;/span> &lt;span class="n">ciphertext&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="s1">&amp;#39;&amp;#39;&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">join&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">plain&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 실행 예시&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">if&lt;/span> &lt;span class="vm">__name__&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="s1">&amp;#39;__main__&amp;#39;&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nb">print&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;--- RSA 암호 토이 구현 ---&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">public_key&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">private_key&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">generate_keypair&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">bits&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">12&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="c1"># 12비트의 소수 사용&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nb">print&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="sa">f&lt;/span>&lt;span class="s2">&amp;#34;공개키 (e, n): &lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">public_key&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nb">print&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="sa">f&lt;/span>&lt;span class="s2">&amp;#34;비밀키 (d, n): &lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">private_key&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">message&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s2">&amp;#34;Hello Math!&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nb">print&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="sa">f&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="se">\n&lt;/span>&lt;span class="s2">원래의 메시지: &lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">message&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># 암호화&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">encrypted_msg&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">encrypt&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">public_key&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">message&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nb">print&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="sa">f&lt;/span>&lt;span class="s2">&amp;#34;암호문: &lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">encrypted_msg&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># 복호화&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">decrypted_msg&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">decrypt&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">private_key&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">encrypted_msg&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nb">print&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="sa">f&lt;/span>&lt;span class="s2">&amp;#34;복호화된 메시지: &lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">decrypted_msg&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>이 코드를 실행하면 문자 배열이 낯선 숫자 배열(암호문)로 변환되고, 그것이 비밀키에 의해 훌륭하게 원래 문자열로 복원되는 모습을 확인할 수 있습니다.&lt;/p>
&lt;hr>
&lt;h2 id="9-마치며-수학의-아름다움과-실용성의-교차점">9. 마치며: 수학의 아름다움과 실용성의 교차점
&lt;/h2>&lt;p>피에르 드 페르마가 이 &amp;lsquo;소정리&amp;rsquo;를 발견한 17세기 당시, 이것이 어딘가에 쓸모가 있을 것이라고 생각한 사람은 아무도 없었습니다. 페르마 자신도 순수한 수학적 탐구심에서 정수론 연구를 하고 있었습니다.&lt;/p>
&lt;p>하지만 약 300년 후인 1970년대, 컴퓨터 네트워크의 여명기에 안전한 통신 프로토콜을 확립하기 위해 없어서는 안 될 암호 기술로서 페르마의 정리는 극적인 부활을 이뤘습니다. 페르마의 소정리에 기반한 소수 판별 기술과 오일러의 정리에 기반한 RSA 암호는 현대의 인터넷 인프라를 말 그대로 지탱하고 있습니다.&lt;/p>
&lt;p>우리가 매일 무심코 보내는 메신저의 메시지도, 쇼핑몰에서의 물건 구매도, 모든 것은 이 $a^{p-1} \equiv 1 \pmod p$ 라는 심플하고 아름다운 수식 위에서 춤추고 있는 것입니다. 수학이 아무리 추상적이라 할지라도 언젠가는 반드시 인류에게 도움이 될 때가 온다는 것을 페르마의 소정리는 가르쳐 주고 있습니다.&lt;/p>
&lt;p>프로그래밍이나 암호 이론을 배울 때 그 기초에 있는 수학적 구조를 이해하는 것은 블랙박스로 제공되는 라이브러리의 동작을 깊이 이해하고, 더 안전한 시스템을 설계하기 위한 큰 무기가 될 것입니다.&lt;/p></description></item><item><title>격자 암호(Lattice-based cryptography)의 수학적 직관</title><link>http://kenji.blog/ko/p/lattice-based-cryptography-math-intuition/</link><pubDate>Fri, 11 Sep 2026 21:00:00 +0900</pubDate><guid>http://kenji.blog/ko/p/lattice-based-cryptography-math-intuition/</guid><description>&lt;img src="http://kenji.blog/p/lattice-based-cryptography-math-intuition/img/eyecatch.jpg" alt="Featured image of post 격자 암호(Lattice-based cryptography)의 수학적 직관" />&lt;h1 id="1-도입-포스트-양자-암호pqc의-여명과-격자-암호의-대두">1. 도입: 포스트 양자 암호(PQC)의 여명과 격자 암호의 대두
&lt;/h1>&lt;p>현대 사회의 디지털 인프라를 지탱하고 있는 것은 RSA 암호나 타원곡선암호(ECC)를 비롯한 공개키 암호 기술입니다. 이러한 암호 방식은 &amp;lsquo;소인수분해 문제&amp;rsquo;나 &amp;lsquo;이산대수 문제&amp;rsquo;와 같은, 기존의 고전 컴퓨터로는 효율적으로 풀 수 없다고(지수 함수적인 시간이 소요된다고) 믿어지는 수학적 난해성에 안전성의 근거를 두고 있습니다.&lt;/p>
&lt;p>그러나 1994년 피터 쇼어(Peter Shor)가 발표한 &amp;lsquo;쇼어 알고리즘(Shor&amp;rsquo;s algorithm)&amp;lsquo;은 암호계에 큰 충격을 주었습니다. 이 알고리즘은 대규모 양자 컴퓨터가 실현될 경우 소인수분해 문제나 이산대수 문제를 다항식 시간 내에 풀어버린다는 것을 수학적으로 증명한 것입니다. 이는 즉, 현재 널리 이용되고 있는 공개키 암호가 장래에는 완전히 해독 가능해진다는 것을 의미합니다.&lt;/p>
&lt;p>이러한 &amp;lsquo;양자 컴퓨터의 위협(Quantum Threat)&amp;lsquo;에 대응하기 위해, 양자 컴퓨터를 사용해도 해독이 어려운 새로운 암호 방식의 연구가 시급해졌습니다. 이것이 &amp;lsquo;포스트 양자 암호(Post-Quantum Cryptography: PQC)&amp;rsquo; 또는 &amp;lsquo;양자 내성 암호&amp;rsquo;라고 불리는 분야입니다.&lt;/p>
&lt;p>PQC에는 몇 가지 유력한 후보가 존재합니다. 해시 기반 암호, 코드 기반 암호, 다변수 다항식 암호, 아이소제니 기반 암호 등을 들 수 있지만, 그 중에서도 현재 가장 주목받고 있으며 NIST(미국 국립표준기술연구소)의 PQC 표준화 프로세스의 중심이 되고 있는 것이 바로 &amp;lsquo;격자 암호(Lattice-based cryptography)&amp;lsquo;입니다. 격자 암호는 다른 방식과 비교하여 암호화 및 복호화 처리 속도가 매우 빠르며, 또한 &amp;lsquo;최악의 경우 복잡도(Worst-case complexity)&amp;lsquo;를 &amp;lsquo;평균적인 경우 복잡도(Average-case complexity)&amp;lsquo;로 환원하는, 암호 이론에 있어서 극히 강력한 안전성 증명을 갖는다는 두드러진 특징을 가지고 있습니다.&lt;/p>
&lt;p>본 기사에서는 이 격자 암호의 기초가 되는 &amp;lsquo;격자(Lattice)&amp;lsquo;의 수학적 정의부터 출발하여, 격자 상의 어려운 문제인 SVP(최단 벡터 문제)나 CVP(최근접 벡터 문제), 그리고 현대 격자 암호의 심장부라고도 할 수 있는 &amp;lsquo;LWE 문제(Learning With Errors)&amp;lsquo;에 대해 수식과 기하학적인 직관, 그리고 구체적인 수치 예를 섞어 철저하고 깊이 있게 해설하겠습니다.&lt;/p>
&lt;h1 id="2-격자lattice의-수학적-정의와-기하학적-직관">2. 격자(Lattice)의 수학적 정의와 기하학적 직관
&lt;/h1>&lt;h2 id="21-벡터-공간과-격자">2.1 벡터 공간과 격자
&lt;/h2>&lt;p>수학에서 &amp;lsquo;격자(Lattice)&amp;lsquo;란 $n$차원 실수 벡터 공간 $\mathbb{R}^n$ 내에 규칙적으로 늘어선 이산적인 점들의 집합을 말합니다. 선형대수학에서 배우는 벡터 공간(Vector Space)과 비슷하지만 결정적인 차이가 있습니다. 벡터 공간이 기저 벡터의 &amp;lsquo;실수 계수&amp;rsquo;를 통한 선형 결합으로 표현되는 연속적인 공간인 반면, 격자는 기저 벡터의 &amp;lsquo;정수 계수&amp;rsquo;를 통한 선형 결합으로 표현되는 이산적인 공간입니다.&lt;/p>
&lt;p>수학적으로 엄밀한 정의를 내려보겠습니다. $m$차원 실수 벡터 공간 $\mathbb{R}^m$에서의 $n$개($n \le m$)의 선형 독립적인 벡터 $\mathbf{b}_1, \mathbf{b}_2, \dots, \mathbf{b}_n$을 생각해 봅시다. 이들 벡터를 열벡터로 가지는 행렬을 $B = [\mathbf{b}_1, \mathbf{b}_2, \dots, \mathbf{b}_n] \in \mathbb{R}^{m \times n}$이라고 합니다. 이 $B$를 격자의 &amp;lsquo;기저(Basis)&amp;lsquo;라고 부릅니다.&lt;/p>
&lt;p>이 기저 $B$에 의해 생성되는 격자 $\mathcal{L}(B)$는 다음과 같이 정의됩니다.&lt;/p>
$$
\mathcal{L}(B) = \left\{ \sum_{i=1}^{n} x_i \mathbf{b}_i \mathrel{\bigg|} x_i \in \mathbb{Z} \right\} = \{ B \mathbf{x} \mid \mathbf{x} \in \mathbb{Z}^n \}
$$
&lt;p>여기서 중요한 것은 계수 $x_i$가 실수 $\mathbb{R}$이 아니라 정수 $\mathbb{Z}$로 한정되어 있다는 점입니다. 이로 인해 공간 내에 무수히 존재하는 연속적인 점이 아니라, 일정한 간격으로 배치된 교차점과 같은 &amp;lsquo;이산적인 점들의 집합&amp;rsquo;이 형성됩니다.&lt;/p>
&lt;h2 id="22-기하학적-이미지">2.2 기하학적 이미지
&lt;/h2>&lt;p>2차원 평면 $\mathbb{R}^2$의 예로 생각해 봅시다. 기저 벡터로서 $\mathbf{b}_1 = \begin{pmatrix} 1 \\ 0 \end{pmatrix}$와 $\mathbf{b}_2 = \begin{pmatrix} 0 \\ 1 \end{pmatrix}$을 선택한 경우, 이들에 의해 생성되는 격자는 좌표평면 상의 모든 정수 좌표 $(x, y) \in \mathbb{Z}^2$의 집합이 됩니다. 이는 가장 단순한 &amp;lsquo;정방격자&amp;rsquo;입니다.&lt;/p>
&lt;p>그러나 격자가 항상 직교하는 것은 아닙니다. 예를 들어, $\mathbf{b}_1 = \begin{pmatrix} 2 \\ 1 \end{pmatrix}$과 $\mathbf{b}_2 = \begin{pmatrix} 1 \\ 3 \end{pmatrix}$이라는 기저를 생각해보면, 생성되는 점은 비스듬하게 왜곡된 그물망의 교차점처럼 됩니다.&lt;/p>
&lt;h2 id="23-기저의-비유일성과-유니모듈러-변환">2.3 기저의 비유일성과 유니모듈러 변환
&lt;/h2>&lt;p>격자 암호의 안전성 근간과 관련된 중요한 성질이 있습니다. 그것은 &amp;lsquo;동일한 격자를 생성하는 기저는 무수히 존재한다&amp;rsquo;는 것입니다.&lt;/p>
&lt;p>예를 들어, 앞서 설명한 $\mathbf{b}_1 = (1, 0)^T, \mathbf{b}_2 = (0, 1)^T$라는 기저가 생성하는 $\mathbb{Z}^2$ 격자는 $\mathbf{b}'_1 = (1, 1)^T, \mathbf{b}'_2 = (2, 3)^T$라는 기저를 사용해도 완전히 같은 격자 $\mathbb{Z}^2$를 생성합니다.&lt;/p>
&lt;p>어떤 기저 $B$와 다른 기저 $B'$가 같은 격자를 생성하기 위한 필요충분조건은, 정수 성분을 가진 행렬 $U \in \mathbb{Z}^{n \times n}$이면서 행렬식이 $\det(U) = \pm 1$이 되는 것이 존재하여,
&lt;/p>
$$ B' = B U $$
&lt;p>
로 표현될 수 있다는 것입니다. 이러한 행렬 $U$를 &amp;lsquo;유니모듈러 행렬(Unimodular matrix)&amp;lsquo;이라고 부릅니다.&lt;/p>
&lt;p>암호에 응용할 때의 기본적인 아이디어는 &amp;lsquo;좋은 기저(직교에 가깝고 짧은 벡터로 이루어진 기저)&amp;lsquo;를 비밀키로 하고, &amp;lsquo;나쁜 기저(서로 극단적으로 비스듬하게 교차하며 매우 긴 벡터로 이루어진 기저)&amp;lsquo;를 공개키로 사용하는 것입니다. 나쁜 기저로부터 좋은 기저를 계산해내는 것은 차원이 높아질수록 매우 어려워집니다. 이것이 격자 암호의 기본적인 직관입니다.&lt;/p>
&lt;h1 id="3-격자에서의-계산이-어려운-문제">3. 격자에서의 계산이 어려운 문제
&lt;/h1>&lt;p>격자 암호의 안전성은 격자 상의 특정한 수학적 문제를 푸는 것의 어려움에 의존하고 있습니다. 여기서는 가장 기본적이면서 유명한 두 가지 문제를 소개합니다.&lt;/p>
&lt;h2 id="31-최단-벡터-문제shortest-vector-problem-svp">3.1 최단 벡터 문제(Shortest Vector Problem: SVP)
&lt;/h2>&lt;p>SVP는 격자 이론에서 가장 고전적이고 유명한 문제입니다.&lt;/p>
&lt;p>&lt;strong>정의 (SVP):&lt;/strong>
임의의 격자 기저 $B$가 주어졌을 때, 그 격자 $\mathcal{L}(B)$에 속하는 영(zero)이 아닌 벡터 중에서 유클리드 노름(길이)이 최소가 되는 벡터 $\mathbf{v}$를 찾아라.&lt;/p>
&lt;p>수식으로 표현하면, $\min_{\mathbf{v} \in \mathcal{L}(B) \setminus \{\mathbf{0}\}} \| \mathbf{v} \|$가 되는 $\mathbf{v}$를 구하는 문제입니다. 이 최소 길이를 $\lambda_1(\mathcal{L})$로 쓰고, &amp;lsquo;격자의 제1연속 최소값(First successive minimum)&amp;lsquo;이라고 부릅니다.&lt;/p>
&lt;p>2차원이나 3차원처럼 낮은 차원이라면, 그림을 그려서 눈으로 가장 짧은 벡터를 찾을 수 있습니다. 또는 가우스의 격자 축소 알고리즘(Gauss lattice reduction algorithm) 등을 사용하여 효율적으로 풀 수 있습니다. 그러나 차원 $n$이 수백에서 수천과 같은 고차원이 되면, SVP를 엄밀하게 푸는 것은 NP-hard임이 알려져 있습니다.&lt;/p>
&lt;p>실제 암호에서는 엄밀한 최단 벡터가 아니라, &amp;lsquo;근사적으로 짧은 벡터&amp;rsquo;를 찾는 근사 SVP($\gamma$-SVP)가 사용됩니다. 근사 계수 $\gamma$가 다항식 크기일 경우, 이 문제는 여전히 매우 어려운 것으로 간주됩니다.&lt;/p>
&lt;h2 id="32-최근접-벡터-문제closest-vector-problem-cvp">3.2 최근접 벡터 문제(Closest Vector Problem: CVP)
&lt;/h2>&lt;p>CVP 또한 격자 암호에 있어서 매우 중요한 문제입니다.&lt;/p>
&lt;p>&lt;strong>정의 (CVP):&lt;/strong>
임의의 격자 기저 $B$와, 공간 내의 임의의 타겟 벡터 $\mathbf{t} \in \mathbb{R}^m$ (반드시 격자점일 필요는 없음)이 주어졌을 때, 격자점 중에서 $\mathbf{t}$에 가장 가까운 격자점 $\mathbf{v} \in \mathcal{L}(B)$를 찾아라.&lt;/p>
&lt;p>수식으로 표현하면, $\min_{\mathbf{v} \in \mathcal{L}(B)} \| \mathbf{v} - \mathbf{t} \|$가 되는 격자점 $\mathbf{v}$를 찾는 문제입니다.&lt;/p>
&lt;p>CVP 역시 SVP와 마찬가지로 고차원에서는 NP-hard입니다. 암호에의 응용이라는 관점에서, 후술할 LWE 문제는 이 CVP의 특수한 변형(Bounded Distance Decoding: BDD)과 밀접한 관계가 있습니다.&lt;/p>
&lt;h2 id="33-왜-고차원이-되면-풀-수-없는가-lll과-bkz의-한계">3.3 왜 고차원이 되면 풀 수 없는가? (LLL과 BKZ의 한계)
&lt;/h2>&lt;p>고차원의 격자 문제를 풀기 위한 유명한 알고리즘으로 LLL 알고리즘(Lenstra-Lenstra-Lovász algorithm)이 있습니다. LLL 알고리즘은 다항식 시간에 동작하며, 격자 기저를 어느 정도 &amp;lsquo;좋은 기저&amp;rsquo;로 축소(Reduction)할 수 있습니다. 그러나 LLL 알고리즘이 찾을 수 있는 최단 벡터는 진정한 최단 벡터의 길이에 대해 지수 함수적인($2^{\mathcal{O}(n)}$) 근사 계수를 가지기 때문에, 암호의 안전성을 깨뜨리기에는 역부족입니다.&lt;/p>
&lt;p>LLL을 개량한 BKZ(Block Korkine-Zolotarev) 알고리즘과 같은 더 강력한 기저 축소 알고리즘을 사용하면 더 짧은 벡터를 찾을 수 있지만, 그 계산량은 블록 크기에 대해 지수 함수적으로 증가합니다. 격자 암호에서는 이 BKZ 알고리즘의 실행 시간을 추정함으로써 안전한 파라미터(차원 $n$의 크기 등)를 결정하고 있습니다. 현재 PQC의 표준 파라미터에서는 차원 $n$이 500에서 1000 이상의 값으로 선택되고 있으며, 슈퍼컴퓨터나 미래의 양자 컴퓨터를 사용하더라도 해독하는 데에는 우주의 나이 이상의 시간이 걸릴 것으로 여겨집니다.&lt;/p>
&lt;h1 id="4-lwe-문제learning-with-errors의-수학적-정식화">4. LWE 문제(Learning With Errors)의 수학적 정식화
&lt;/h1>&lt;p>현대 격자 암호의 대부분은 2005년 오데드 레게브(Oded Regev)가 제안한 &amp;lsquo;LWE 문제(Learning With Errors)&amp;lsquo;를 기반으로 하고 있습니다. LWE 문제의 아름다움은 그 정식화의 단순함과 &amp;lsquo;최악의 경우 복잡도에서 평균적인 경우 복잡도로의 환원&amp;rsquo;이라는 강력한 수학적 증명을 가지고 있다는 점에 있습니다.&lt;/p>
&lt;h2 id="41-노이즈가-없는-연립-일차-방정식">4.1 노이즈가 없는 연립 일차 방정식
&lt;/h2>&lt;p>LWE 문제를 이해하기 위해, 우선 노이즈가 없는 단순한 연립 일차 방정식을 생각해 봅시다.
미지의 비밀 벡터 $\mathbf{s} \in \mathbb{Z}_q^n$ (각 성분은 $0$부터 $q-1$까지의 정수)가 있다고 가정합니다. 여기서 $q$는 소수라고 합니다.&lt;/p>
&lt;p>랜덤한 계수 벡터 $\mathbf{a}_1, \mathbf{a}_2, \dots \in \mathbb{Z}_q^n$을 선택하고, 비밀 벡터 $\mathbf{s}$와의 내적을 법 $q$로 계산합니다.
$b_1 = \langle \mathbf{a}_1, \mathbf{s} \rangle \pmod q$
$b_2 = \langle \mathbf{a}_2, \mathbf{s} \rangle \pmod q$
$\vdots$&lt;/p>
&lt;p>충분한 수($n$개 이상)의 $(\mathbf{a}_i, b_i)$ 쌍이 주어질 경우, 우리는 선형대수의 &amp;lsquo;가우스 소거법(Gaussian elimination)&amp;lsquo;을 사용하여 쉽게 비밀 벡터 $\mathbf{s}$를 복원할 수 있습니다. 이는 다항식 시간 내에 간단히 풀 수 있는 문제입니다.&lt;/p>
&lt;h2 id="42-lwe-문제의-정의-노이즈를-더하다">4.2 LWE 문제의 정의: 노이즈를 더하다
&lt;/h2>&lt;p>그렇다면, 이 문제에 약간의 &amp;lsquo;노이즈(오차)&amp;lsquo;를 더하면 어떻게 될까요?
이것이 LWE 문제의 본질입니다.&lt;/p>
&lt;p>미지의 비밀 벡터 $\mathbf{s} \in \mathbb{Z}_q^n$에 대하여, 각 방정식의 결과에 작은 오차 $e_i \in \mathbb{Z}_q$를 더합니다.
$b_i = \langle \mathbf{a}_i, \mathbf{s} \rangle + e_i \pmod q$&lt;/p>
&lt;p>여기서 $e_i$는 평균이 0이고 표준편차가 비교적 작은(예를 들어 정규분포와 같은, 이산 가우스 분포에서 선택된) 작은 정수값입니다.
주어지는 정보는 랜덤한 벡터 $\mathbf{a}_i$와, 거기에 오차를 더해 계산된 $b_i$ 쌍의 리스트입니다.
$( \mathbf{a}_1, b_1 ), ( \mathbf{a}_2, b_2 ), \dots, ( \mathbf{a}_m, b_m )$&lt;/p>
&lt;p>이를 행렬로 표현하면 매우 깔끔해집니다.
랜덤한 행렬 $A \in \mathbb{Z}_q^{m \times n}$, 비밀 벡터 $\mathbf{s} \in \mathbb{Z}_q^n$, 오차 벡터 $\mathbf{e} \in \mathbb{Z}_q^m$을 사용하여,
&lt;/p>
$$ \mathbf{b} = A \mathbf{s} + \mathbf{e} \pmod q $$
&lt;p>
라고 쓸 수 있습니다. 주어지는 것은 $A$와 $\mathbf{b}$뿐입니다. 여기서 $\mathbf{s}$를 구하는 것이 &amp;lsquo;탐색 LWE 문제(Search LWE problem)&amp;lsquo;입니다.&lt;/p>
&lt;p>오차 $e_i$가 들어있기 때문에, 가우스 소거법을 사용하려고 하면 방정식을 더하고 빼는 과정에서 오차가 지수 함수적으로 증폭되어 올바른 답에 도달할 수 없게 됩니다. 얼핏 보면 단순한 연립 일차 방정식처럼 보이지만, 이 작은 노이즈가 더해지는 것만으로 문제의 난이도가 NP-hard 수준으로 뛰어오르게 됩니다.&lt;/p>
&lt;h2 id="43-결정-lwe-문제decision-lwe">4.3 결정 LWE 문제(Decision LWE)
&lt;/h2>&lt;p>암호 이론의 증명에서 빈번하게 사용되는 것은 탐색 LWE 문제의 변형인 &amp;lsquo;결정 LWE 문제(Decision LWE problem)&amp;lsquo;입니다.&lt;/p>
&lt;p>결정 LWE 문제란, 다음의 2가지 분포에서 얻은 샘플의 리스트가 주어졌을 때, 그것이 어느 분포에서 온 것인지를 판정하는 문제입니다.&lt;/p>
&lt;ol>
&lt;li>&lt;strong>LWE 분포&lt;/strong>: 의도적으로 계산된 $(A, \mathbf{b} = A\mathbf{s} + \mathbf{e} \pmod q)$&lt;/li>
&lt;li>&lt;strong>균등 랜덤 분포&lt;/strong>: 완전히 랜덤하게 선택된 행렬 $A$와 벡터 $\mathbf{u}$로 이루어진 $(A, \mathbf{u})$&lt;/li>
&lt;/ol>
&lt;p>놀랍게도, LWE 문제의 파라미터를 적절히 선택하면, LWE 분포에서 얻은 쌍은 완전히 랜덤한 데이터 쌍과 &amp;lsquo;계산량적으로 식별 불가능(Computationally Indistinguishable)&amp;lsquo;해집니다. 이 성질이 LWE 기반 암호가 &amp;lsquo;난수와 구별할 수 없는 암호문&amp;rsquo;을 생성할 수 있는 근거가 됩니다.&lt;/p>
&lt;h2 id="44-최악의-경우-복잡도에서-평균적인-경우-복잡도로의-환원레게브의-정리">4.4 최악의 경우 복잡도에서 평균적인 경우 복잡도로의 환원(레게브의 정리)
&lt;/h2>&lt;p>오데드 레게브의 가장 큰 공적은 이 LWE 문제의 어려움을 앞서 언급한 격자 문제(SVP나 CVP)의 어려움에 수학적으로 연결시킨 것입니다.&lt;/p>
&lt;p>그는 양자 환원(Quantum reduction)을 사용하여, &amp;lsquo;만약 LWE 문제를 평균적으로(랜덤하게 선택된 $A$와 $\mathbf{e}$에 대해) 풀 수 있는 다항식 시간 알고리즘이 존재한다면, 임의의 격자의 최악의 경우(가장 어려운 경우)의 Gap-SVP를 풀 수 있는 다항식 시간의 양자 알고리즘이 존재한다&amp;rsquo;는 것을 증명했습니다. (나중에 페이커트(Peikert) 등에 의해 고전적인 환원도 증명되었습니다.)&lt;/p>
&lt;p>이는 암호 이론에 있어 꿈과 같은 성질입니다. 왜냐하면, &amp;lsquo;암호가 깨지는 것은 우리가 우연히 약한 키(평균적인 경우의 일부)를 선택해 버렸기 때문일지도 모른다&amp;rsquo;는 우려를 불식시키고, &amp;lsquo;평균적인 LWE를 풀 수 있다면, 격자의 모든 어려운 문제를 풀 수 있게 된다(그러므로 LWE는 절대적으로 어렵다)&amp;lsquo;는 강력한 보증을 해주기 때문입니다.&lt;/p>
&lt;div class="mermaid">graph TD
A["최악의 경우 격자 문제 (Gap-SVP, SIVP)"] -->|양자/고전적 환원| B["평균적인 경우 LWE 문제"]
B -->|암호학적 구성| C["LWE 기반 암호 시스템 (PKE, KEM, FHE)"]
style A fill:#ffcccc,stroke:#ff0000,stroke-width:2px,color:#000
style B fill:#ccffcc,stroke:#00aa00,stroke-width:2px,color:#000
style C fill:#ccccff,stroke:#0000ff,stroke-width:2px,color:#000&lt;/div>
&lt;h1 id="5-lwe를-이용한-공개키-암호-방식레게브-암호의-구성">5. LWE를 이용한 공개키 암호 방식(레게브 암호)의 구성
&lt;/h1>&lt;p>LWE 문제의 어려움을 이해했으니, 그것을 사용하여 어떻게 암호화와 복호화를 수행하는지, 오데드 레게브가 제안한 기본적인 공개키 암호 방식을 살펴봅시다. 여기서는 1비트의 메시지 $M \in \{0, 1\}$를 암호화하는 가장 기본적인 메커니즘을 설명합니다.&lt;/p>
&lt;h2 id="51-키-생성key-generation">5.1 키 생성(Key Generation)
&lt;/h2>&lt;ol>
&lt;li>시스템 파라미터로서 법이 되는 소수 $q$, 차원 $n$, 방정식의 수 $m$($m > n \log q$)을 결정합니다.&lt;/li>
&lt;li>비밀키로서 벡터 $\mathbf{s} \in \mathbb{Z}_q^n$를 랜덤하게 선택합니다.&lt;/li>
&lt;li>랜덤한 행렬 $A \in \mathbb{Z}_q^{m \times n}$을 생성합니다.&lt;/li>
&lt;li>작은 오차 벡터 $\mathbf{e} \in \mathbb{Z}_q^m$를 이산 가우스 분포 등의 오차 분포에서 선택합니다.&lt;/li>
&lt;li>벡터 $\mathbf{b} = A \mathbf{s} + \mathbf{e} \pmod q$를 계산합니다.&lt;/li>
&lt;li>공개키(Public Key)는 $(A, \mathbf{b})$가 됩니다.&lt;/li>
&lt;li>비밀키(Secret Key)는 $\mathbf{s}$가 됩니다.&lt;/li>
&lt;/ol>
&lt;p>공개키는 그야말로 &amp;lsquo;LWE 문제의 인스턴스&amp;rsquo; 그 자체입니다. 공개키 $(A, \mathbf{b})$로부터 비밀키 $\mathbf{s}$를 구하는 것은 탐색 LWE 문제를 푸는 것과 같기 때문에 안전성이 보장됩니다.&lt;/p>
&lt;h2 id="52-암호화encryption">5.2 암호화(Encryption)
&lt;/h2>&lt;p>앨리스는 밥의 공개키 $(A, \mathbf{b})$를 사용하여, 1비트의 메시지 $M \in \{0, 1\}$를 암호화합니다.&lt;/p>
&lt;ol>
&lt;li>랜덤한 이진 벡터(성분이 0 또는 1) $\mathbf{r} \in \{0, 1\}^m$를 선택합니다.&lt;/li>
&lt;li>암호문의 전반부로서 벡터 $\mathbf{u} = A^T \mathbf{r} \pmod q$를 계산합니다. ($A^T$는 $A$의 전치 행렬입니다. 즉, $A$의 행 중에서 $\mathbf{r}$의 성분이 1인 행들을 더하는 것입니다).&lt;/li>
&lt;li>암호문의 후반부로서 스칼라 $v = \mathbf{b}^T \mathbf{r} + M \cdot \lfloor \frac{q}{2} \rfloor \pmod q$를 계산합니다.
(메시지 $M$이 0이면 아무것도 더하지 않고, $1$이면 $q$의 정확히 절반 값인 $\lfloor \frac{q}{2} \rfloor$를 더합니다).&lt;/li>
&lt;li>암호문(Ciphertext)은 $(\mathbf{u}, v)$가 됩니다.&lt;/li>
&lt;/ol>
&lt;p>암호화의 직관적인 의미는 공개키의 행렬 $A$와 벡터 $\mathbf{b}$에 대하여 &amp;lsquo;랜덤한 부분 집합의 합&amp;rsquo;을 취하는 것입니다. 결정 LWE 문제의 어려움에 의해 이 암호문 $(\mathbf{u}, v)$는 완전히 랜덤한 벡터 및 균등 난수와 구별이 불가능한 것처럼 보입니다(의미론적 안전성: Semantic Security).&lt;/p>
&lt;div class="mermaid">flowchart LR
M["메시지 M in {0,1}"] --> Enc
PK["공개키 (A, b)"] --> Enc
r["랜덤 이진 벡터 r"] --> Enc
subgraph Enc ["암호화 과정"]
direction TB
u_calc["u = A^T * r mod q"]
v_calc["v = b^T * r + M * floor(q/2) mod q"]
end
Enc --> CT["암호문 (u, v)"]&lt;/div>
&lt;h2 id="53-복호화decryption">5.3 복호화(Decryption)
&lt;/h2>&lt;p>밥은 비밀키 $\mathbf{s}$를 사용하여 암호문 $(\mathbf{u}, v)$를 복호화합니다.&lt;/p>
&lt;ol>
&lt;li>다음 값을 계산합니다: $D = v - \mathbf{s}^T \mathbf{u} \pmod q$&lt;/li>
&lt;li>계산한 결과가 $0$에 가까우면 $M=0$, $\lfloor \frac{q}{2} \rfloor$에 가까우면 $M=1$로 출력합니다.&lt;/li>
&lt;/ol>
&lt;p>왜 이렇게 복호화할 수 있는지 수학적으로 전개해 봅시다.
$\mathbf{b} = A \mathbf{s} + \mathbf{e}$ 였음을 상기해 주십시오.&lt;/p>
$$
\begin{aligned}
v - \mathbf{s}^T \mathbf{u} &amp;= (\mathbf{b}^T \mathbf{r} + M \cdot \lfloor \frac{q}{2} \rfloor) - \mathbf{s}^T (A^T \mathbf{r}) \\
&amp;= ((A \mathbf{s} + \mathbf{e})^T \mathbf{r} + M \cdot \lfloor \frac{q}{2} \rfloor) - \mathbf{s}^T A^T \mathbf{r} \\
&amp;= (\mathbf{s}^T A^T \mathbf{r} + \mathbf{e}^T \mathbf{r} + M \cdot \lfloor \frac{q}{2} \rfloor) - \mathbf{s}^T A^T \mathbf{r} \\
&amp;= \mathbf{e}^T \mathbf{r} + M \cdot \lfloor \frac{q}{2} \rfloor \pmod q
\end{aligned}
$$
&lt;p>여기서, 식 안에서 $\mathbf{s}^T A^T \mathbf{r}$가 깔끔하게 상쇄되어 사라졌습니다!
남은 것은 $\mathbf{e}^T \mathbf{r} + M \cdot \lfloor \frac{q}{2} \rfloor$ 입니다.&lt;/p>
&lt;p>$\mathbf{e}$는 성분이 매우 작은 노이즈 벡터이고, $\mathbf{r}$는 성분이 0 또는 1인 이진 벡터입니다. 따라서 이들의 내적인 $\mathbf{e}^T \mathbf{r}$도 (파라미터를 적절히 선택하면) 비교적 작은 값에 머물게 됩니다.&lt;/p>
&lt;ul>
&lt;li>만약 $M=0$이라면 결과는 $\mathbf{e}^T \mathbf{r}$가 되어 $0$에 가까운 작은 값이 됩니다.&lt;/li>
&lt;li>만약 $M=1$이라면 결과는 $\mathbf{e}^T \mathbf{r} + \lfloor \frac{q}{2} \rfloor$가 되어 $q$의 절반 값인 $\lfloor \frac{q}{2} \rfloor$의 주변에 위치하게 됩니다.&lt;/li>
&lt;/ul>
&lt;p>오차 $\mathbf{e}^T \mathbf{r}$의 절댓값이 $\frac{q}{4}$ 미만으로 수렴하도록 파라미터가 설계되어 있다면, 밥은 계산 결과가 $0$과 $\lfloor \frac{q}{2} \rfloor$ 중 어느 쪽에 더 가까운지를 보는 것만으로 메시지 $M$을 정확하게 판정(복호화)할 수 있습니다. 이것이 LWE 기반 암호가 기능하는 아름다운 메커니즘입니다.&lt;/p>
&lt;div class="mermaid">flowchart LR
CT["암호문 (u, v)"] --> Dec
SK["비밀키 s"] --> Dec
subgraph Dec ["복호화 과정"]
direction TB
calc["D = v - s^T * u mod q 계산"]
check["D가 0 또는 q/2 중 어디에 가까운지 확인"]
end
calc --> check
Dec --> M_out["복원된 메시지 M"]&lt;/div>
&lt;h1 id="6-구체적인-수치를-이용한-lwe-암호의-토이-이그잼플">6. 구체적인 수치를 이용한 LWE 암호의 토이 이그잼플
&lt;/h1>&lt;p>수식의 나열만으로는 실감하기 어려울 것이라 생각하므로, 실제로 매우 작은 수치 파라미터를 설정하여 암호화부터 복호화까지의 계산을 따라가 보겠습니다.
(※실제 암호 시스템에서는 안전성 확보를 위해 $n$은 500 이상, $q$는 수천 이상의 값이 사용됩니다)&lt;/p>
&lt;p>&lt;strong>【파라미터 설정】&lt;/strong>&lt;/p>
&lt;ul>
&lt;li>법 $q = 17$ (소수. 따라서 값은 $0$부터 $16$까지의 범위를 갖습니다)&lt;/li>
&lt;li>차원 $n = 2$&lt;/li>
&lt;li>방정식의 수 $m = 4$&lt;/li>
&lt;li>메시지 $M = 1$을 암호화한다고 가정합니다.&lt;/li>
&lt;li>메시지의 시프트 양: $\lfloor \frac{q}{2} \rfloor = \lfloor \frac{17}{2} \rfloor = 8$&lt;/li>
&lt;/ul>
&lt;p>&lt;strong>【1. 키 생성 단계】&lt;/strong>
밥은 비밀키 $\mathbf{s}$와 행렬 $A$, 오차 벡터 $\mathbf{e}$를 랜덤하게 선택합니다.
&lt;/p>
$$ \mathbf{s} = \begin{pmatrix} 3 \\ 4 \end{pmatrix} \in \mathbb{Z}_{17}^2 $$
$$ A = \begin{pmatrix} 2 &amp; 15 \\ 1 &amp; 8 \\ 14 &amp; 5 \\ 9 &amp; 10 \end{pmatrix} \in \mathbb{Z}_{17}^{4 \times 2} $$
$$ \mathbf{e} = \begin{pmatrix} 1 \\ -1 \\ 0 \\ 2 \end{pmatrix} \equiv \begin{pmatrix} 1 \\ 16 \\ 0 \\ 2 \end{pmatrix} \pmod{17} $$
&lt;p>다음으로 공개키 $\mathbf{b}$를 계산합니다.
&lt;/p>
$$ A \mathbf{s} = \begin{pmatrix} 2 &amp; 15 \\ 1 &amp; 8 \\ 14 &amp; 5 \\ 9 &amp; 10 \end{pmatrix} \begin{pmatrix} 3 \\ 4 \end{pmatrix} = \begin{pmatrix} 2\times 3 + 15\times 4 \\ 1\times 3 + 8\times 4 \\ 14\times 3 + 5\times 4 \\ 9\times 3 + 10\times 4 \end{pmatrix} = \begin{pmatrix} 6 + 60 \\ 3 + 32 \\ 42 + 20 \\ 27 + 40 \end{pmatrix} = \begin{pmatrix} 66 \\ 35 \\ 62 \\ 67 \end{pmatrix} $$
&lt;p>
이를 법 17로 계산합니다. ($66 = 17 \times 3 + 15$ 등)
&lt;/p>
$$ A \mathbf{s} \pmod{17} = \begin{pmatrix} 15 \\ 1 \\ 11 \\ 16 \end{pmatrix} $$
&lt;p>
오차 벡터 $\mathbf{e}$를 더합니다.
&lt;/p>
$$ \mathbf{b} = A \mathbf{s} + \mathbf{e} = \begin{pmatrix} 15 \\ 1 \\ 11 \\ 16 \end{pmatrix} + \begin{pmatrix} 1 \\ 16 \\ 0 \\ 2 \end{pmatrix} = \begin{pmatrix} 16 \\ 17 \\ 11 \\ 18 \end{pmatrix} \equiv \begin{pmatrix} 16 \\ 0 \\ 11 \\ 1 \end{pmatrix} \pmod{17} $$
&lt;p>공개키는 $A$와 $\mathbf{b} = (16, 0, 11, 1)^T$가 됩니다.&lt;/p>
&lt;p>&lt;strong>【2. 암호화 단계】&lt;/strong>
앨리스는 메시지 $M = 1$을 암호화합니다.
랜덤한 벡터 $\mathbf{r}$를 선택합니다. 여기서는 $\mathbf{r} = (1, 0, 1, 0)^T$로 합니다.&lt;/p>
&lt;p>$\mathbf{u}$를 계산합니다.
&lt;/p>
$$ \mathbf{u} = A^T \mathbf{r} = \begin{pmatrix} 2 &amp; 1 &amp; 14 &amp; 9 \\ 15 &amp; 8 &amp; 5 &amp; 10 \end{pmatrix} \begin{pmatrix} 1 \\ 0 \\ 1 \\ 0 \end{pmatrix} = \begin{pmatrix} 2 \times 1 + 14 \times 1 \\ 15 \times 1 + 5 \times 1 \end{pmatrix} = \begin{pmatrix} 16 \\ 20 \end{pmatrix} \equiv \begin{pmatrix} 16 \\ 3 \end{pmatrix} \pmod{17} $$
&lt;p>$v$를 계산합니다.
&lt;/p>
$$ \mathbf{b}^T \mathbf{r} = (16, 0, 11, 1) \begin{pmatrix} 1 \\ 0 \\ 1 \\ 0 \end{pmatrix} = 16 \times 1 + 11 \times 1 = 27 \equiv 10 \pmod{17} $$
&lt;p>
메시지 $M=1$에 해당하는 값 $\lfloor 17/2 \rfloor = 8$을 더합니다.
&lt;/p>
$$ v = \mathbf{b}^T \mathbf{r} + M \cdot 8 = 10 + 1 \times 8 = 18 \equiv 1 \pmod{17} $$
&lt;p>앨리스는 암호문으로 $(\mathbf{u}, v) = \left( \begin{pmatrix} 16 \\ 3 \end{pmatrix}, 1 \right)$을 밥에게 전송합니다.&lt;/p>
&lt;p>&lt;strong>【3. 복호화 단계】&lt;/strong>
암호문을 받은 밥은 비밀키 $\mathbf{s} = (3, 4)^T$를 사용하여 복호화합니다.
복호화 처리식: $D = v - \mathbf{s}^T \mathbf{u} \pmod{17}$을 계산합니다.&lt;/p>
$$ \mathbf{s}^T \mathbf{u} = (3, 4) \begin{pmatrix} 16 \\ 3 \end{pmatrix} = 3 \times 16 + 4 \times 3 = 48 + 12 = 60 \equiv 9 \pmod{17} $$
$$ D = v - \mathbf{s}^T \mathbf{u} = 1 - 9 = -8 \pmod{17} $$
&lt;p>여기서 법 17의 세계에서는 $-8$이 $9$와 같아집니다($-8 + 17 = 9$).
얻어진 값 $D = 9$를 $0$과 $8$($\lfloor 17/2 \rfloor$) 중 어디에 가까운지 판정합니다.
$9$는 $0$보다 $8$에 명백히 더 가깝기 때문에 밥은 올바르게 $M = 1$을 복원할 수 있었습니다!&lt;/p>
&lt;p>왜 $9$가 되었을까요? 앞서 설명한 증명을 상기해 봅시다.
오차 부분은 $\mathbf{e}^T \mathbf{r} = (1, -1, 0, 2) (1, 0, 1, 0)^T = 1 \times 1 + 0 \times 1 = 1$로 되어 있습니다.
따라서 계산 결과는 $\mathbf{e}^T \mathbf{r} + M \cdot 8 = 1 + 8 = 9$가 되어, 이론대로의 값이 계산되었음을 확인할 수 있었습니다.&lt;/p>
&lt;h1 id="7-실용화를-향한-진화-ring-lwe와-module-lwe">7. 실용화를 향한 진화: Ring-LWE와 Module-LWE
&lt;/h1>&lt;p>지금까지 설명한 표준적인 LWE 문제(Standard LWE)는 매우 강력한 안전성 증명을 가지고 있지만, 실용상 치명적인 약점이 있습니다. 그것은 &amp;lsquo;키의 크기가 거대해진다는 것&amp;rsquo;과 &amp;lsquo;계산 비용이 높다는 것&amp;rsquo;입니다.&lt;/p>
&lt;p>Standard LWE에서는 공개키에 거대한 행렬 $A \in \mathbb{Z}_q^{m \times n}$이 포함됩니다. 파라미터 $n$이 수백에서 수천이 되면 이 행렬의 크기는 수 메가바이트에 달해, 인터넷 상의 통신 프로토콜(TLS 등)에서 매번 송수신하기에는 너무 무겁습니다. 또한 행렬과 벡터의 곱셈에는 $\mathcal{O}(n^2)$의 계산량이 듭니다.&lt;/p>
&lt;p>이 문제를 해결하기 위해 도입된 것이 다항식 환(Polynomial rings)이라는 대수적인 구조를 격자에 결합한 &amp;lsquo;Ring-LWE(RLWE)&amp;lsquo;나 &amp;lsquo;Module-LWE(MLWE)&amp;lsquo;입니다.&lt;/p>
&lt;h2 id="71-ring-lwe의-직관">7.1 Ring-LWE의 직관
&lt;/h2>&lt;p>Ring-LWE에서는 벡터나 행렬을 다항식 환 $\mathcal{R}_q = \mathbb{Z}_q[X]/(X^n + 1)$ 상의 요소(다항식)로 대체합니다. (여기서 $n$은 2의 거듭제곱으로 선택됩니다).&lt;/p>
&lt;p>Standard LWE의 공개키가 행렬 $A$였던 것에 반해, Ring-LWE에서는 단일 다항식 $a(x)$를 사용합니다. 비밀키 $s(x)$나 오차 $e(x)$도 다항식이 됩니다.
방정식은 다음과 같이 됩니다.
&lt;/p>
$$ b(x) = a(x) \cdot s(x) + e(x) \pmod q $$
&lt;p>이는 다항식의 곱셈이므로 고속 푸리에 변환(FFT)과 유사한 &amp;lsquo;수론 변환(Number Theoretic Transform: NTT)&amp;lsquo;을 사용함으로써 계산량을 $\mathcal{O}(n \log n)$까지 극적으로 줄일 수 있습니다. 게다가 공개키의 크기도 행렬에서 단일 다항식으로 작아지기 때문에 데이터 크기가 $\mathcal{O}(n)$으로 감소합니다. 이는 통신 대역폭에 있어서 압도적인 우위를 가져옵니다.&lt;/p>
&lt;p>수학적으로 보면, Ring-LWE는 일반적인 격자가 아니라 &amp;lsquo;아이디얼 격자(Ideal Lattice)&amp;lsquo;라고 불리는 특수한 대칭성을 가진 격자 상의 문제로 귀결됩니다.&lt;/p>
&lt;h2 id="72-module-lwe와-nist의-표준화-kyber--ml-kem">7.2 Module-LWE와 NIST의 표준화 (Kyber / ML-KEM)
&lt;/h2>&lt;p>Ring-LWE는 효율적이지만, 아이디얼 격자의 특수한 대수적 구조가 장래의 공격 실마리가 되지 않을까 하는 일말의 우려가 있었습니다. 그래서 Standard LWE의 보수적인 안전성과 Ring-LWE의 효율성의 &amp;lsquo;장점만을 취한&amp;rsquo; 것이 &amp;lsquo;Module-LWE(MLWE)&amp;lsquo;입니다.&lt;/p>
&lt;p>Module-LWE에서는 다항식을 요소로 하는 작은 행렬과 벡터를 생각합니다. 즉, 환 상의 모듈(가군)을 다룹니다.
현재 NIST가 PQC의 키 캡슐화 메커니즘(KEM) 표준으로 선정한 &amp;lsquo;CRYSTALS-Kyber&amp;rsquo;(표준화 명칭: ML-KEM)는 바로 이 Module-LWE 문제의 어려움에 기반하여 구축되어 있습니다.&lt;/p>
&lt;h1 id="8-왜-양자-컴퓨터에-대해-안전한가">8. 왜 양자 컴퓨터에 대해 안전한가?
&lt;/h1>&lt;p>마지막으로, &amp;lsquo;왜 격자 암호는 양자 컴퓨터를 사용해도 해독되지 않는다고 여겨지는가?&amp;lsquo;라는 핵심 부분을 짚고 넘어가겠습니다.&lt;/p>
&lt;p>양자 컴퓨터가 RSA 암호나 타원곡선암호를 깨는 쇼어 알고리즘은 본질적으로 &amp;lsquo;은닉 부분군 문제(Hidden Subgroup Problem: HSP)&amp;lsquo;를 푸는 알고리즘입니다. RSA나 ECC의 배경에 있는 수학적 구조(유한 아벨 군)는 주기성을 가지고 있으며, 양자 푸리에 변환(QFT)이라는 양자 알고리즘 특유의 조작을 사용함으로써 이 주기(숨겨진 부분군)를 단번에 추출할 수 있습니다.&lt;/p>
&lt;p>그러나 격자 문제는 근본적으로 다릅니다. 격자에도 주기성은 있지만, SVP나 CVP에서 요구되는 것은 &amp;lsquo;최단 거리&amp;rsquo;나 &amp;lsquo;노이즈 제거&amp;rsquo;라는 기하학적이고 비선형적인 성질입니다. 쇼어 알고리즘과 같은 &amp;lsquo;아벨 군 상의 양자 푸리에 변환&amp;rsquo;을 그대로 적용하더라도 격자 문제의 해답이 되는 유용한 정보를 효율적으로 추출할 수 없습니다. 현재까지 SVP나 LWE에 대해 다항식 시간에 풀 수 있는 양자 알고리즘은 발견되지 않았으며, 양자 컴퓨터의 병렬 계산 능력을 동원하더라도 무차별 대입에 가까운 탐색(그로버 알고리즘에 의한 제곱근 수준의 고속화 정도)만이 유효한 수단이라고 널리 믿어지고 있습니다.&lt;/p>
&lt;h1 id="9-요약">9. 요약
&lt;/h1>&lt;p>본 기사에서는 격자 암호의 수학적 직관에 대해 격자의 기하학적 정의부터 시작하여 LWE 문제의 정식화, 그리고 공개키 암호의 구성에 이르기까지 상세히 해설했습니다.&lt;/p>
&lt;ol>
&lt;li>&lt;strong>격자(Lattice)&lt;/strong> 는 기저 벡터의 정수 계수 선형 결합으로 표현되는 이산적인 공간이며, 고차원에서는 직교에 가까운 &amp;lsquo;좋은 기저&amp;rsquo;를 찾는 것(SVP)이 어려워집니다.&lt;/li>
&lt;li>&lt;strong>LWE 문제(Learning With Errors)&lt;/strong> 는 노이즈가 포함된 연립 일차 방정식을 푸는 문제이며, 이것이 격자의 최악의 경우 문제의 어려움에 연결되어 있기 때문에 강력한 안전성의 근거를 제공합니다.&lt;/li>
&lt;li>LWE 문제를 이용함으로써 노이즈를 의도적으로 더하거나 소거하는 교묘한 메커니즘을 통해 암호화 및 복호화(&lt;strong>레게브 암호&lt;/strong>)가 실현됩니다.&lt;/li>
&lt;li>실제 프로토콜에서는 통신 효율과 계산 속도를 높이기 위해 다항식 환을 사용한 &lt;strong>Ring-LWE&lt;/strong>나 &lt;strong>Module-LWE&lt;/strong>가 채택되고 있으며, NIST 표준인 &lt;strong>ML-KEM&lt;/strong>의 기반이 되고 있습니다.&lt;/li>
&lt;/ol>
&lt;p>양자 컴퓨터라는 전대미문의 계산 패러다임 시프트가 다가오는 가운데, 고전적인 선형대수와 정수론의 심연에서 탄생한 &amp;lsquo;격자 암호&amp;rsquo;가 미래 인터넷 보안의 기반을 담당한다는 것은 매우 낭만적인 이야기입니다. 격자 암호의 기초가 되는 수학은 결코 너무 난해한 것이 아니며, 선형대수와 확률에 대한 기초 지식이 있다면 충분히 그 아름다운 구조를 이해할 수 있습니다. 본 기사가 PQC의 핵심이 되는 격자 암호에 대한 이해에 도움이 되기를 바랍니다.&lt;/p></description></item><item><title>리만 가설과 소수의 분포: 현대 암호 기술과의 깊은 관계</title><link>http://kenji.blog/ko/p/riemann-hypothesis-prime-distribution-cryptography/</link><pubDate>Fri, 11 Sep 2026 16:00:00 +0900</pubDate><guid>http://kenji.blog/ko/p/riemann-hypothesis-prime-distribution-cryptography/</guid><description>&lt;img src="http://kenji.blog/p/riemann-hypothesis-prime-distribution-cryptography/img/eyecatch.jpg" alt="Featured image of post 리만 가설과 소수의 분포: 현대 암호 기술과의 깊은 관계" />&lt;h1 id="1-서론-소수라는-우주의-신비와-리만-가설">1. 서론: 소수라는 우주의 신비와 리만 가설
&lt;/h1>&lt;p>&amp;lsquo;소수(Prime Numbers)&amp;lsquo;는 1과 자기 자신으로만 나누어 떨어지는 자연수이며, 수학 세계에서 &amp;lsquo;원자&amp;rsquo;라고도 불립니다. 2, 3, 5, 7, 11, 13&amp;hellip; 으로 이어지는 이 수열은 언뜻 보면 무질서하고 무작위로 나타나는 것처럼 보입니다. 고대 그리스의 수학자 유클리드가 &amp;lsquo;소수가 무한히 존재한다&amp;rsquo;는 것을 증명한 이후, 수많은 수학자들이 이 소수의 배열에 숨겨진 규칙성을 밝혀내기 위해 도전해 왔습니다.&lt;/p>
&lt;p>그 소수의 수수께끼에 가장 근접한 것이 1859년 독일의 수학자 베른하르트 리만(Bernhard Riemann)이 제안한 **&amp;lsquo;리만 가설(Riemann Hypothesis)&amp;rsquo;**입니다. 리만 가설은 현대 수학에서 가장 중요하고 미해결된 난제 중 하나이며, 클레이 수학연구소가 정한 밀레니엄 현상 문제 중 하나로 100만 달러의 상금이 걸려 있습니다.&lt;/p>
&lt;p>언뜻 보기에 소수의 분포에 관한 순수 수학의 난제는 우리의 일상생활과는 무관해 보일지도 모릅니다. 하지만 현대 사회의 인프라를 지탱하는 인터넷 보안, 특히 &lt;strong>RSA 암호나 타원곡선암호(ECC) 같은 현대 암호 기술&lt;/strong>은 거대한 소수의 성질에 깊이 의존하고 있습니다.&lt;/p>
&lt;p>이 글에서는 소수의 분포에서 소수 정리, 리만 제타 함수, 그리고 리만 가설의 핵심에 이르는 수학적 여정을 떠나며, 그것이 어떻게 현대 암호 기술과 연결되어 있는지, 그리고 만약 리만 가설이 증명된다면 세상은 어떻게 될지에 대해 매우 상세하고 깊이 있게 파헤쳐 설명합니다.&lt;/p>
&lt;hr>
&lt;h1 id="2-소수-정리와-소수의-분포-가우스의-발견">2. 소수 정리와 소수의 분포: 가우스의 발견
&lt;/h1>&lt;p>소수가 어떻게 분포되어 있는지를 이해하기 위해, 수학자들은 &amp;lsquo;어떤 수 $x$ 이하의 소수가 몇 개 존재하는가&amp;rsquo;를 나타내는 &lt;strong>소수 계수 함수(Prime-counting function)&lt;/strong> $\pi(x)$ 를 고안했습니다.&lt;/p>
&lt;p>예를 들어:&lt;/p>
&lt;ul>
&lt;li>$\pi(10) = 4$ (2, 3, 5, 7)&lt;/li>
&lt;li>$\pi(100) = 25$&lt;/li>
&lt;li>$\pi(1000) = 168$&lt;/li>
&lt;/ul>
&lt;p>15세의 천재 수학자 카를 프리드리히 가우스(Carl Friedrich Gauss)는 방대한 소수표를 계산하여 소수의 출현 빈도가 자연로그 $\ln x$ 에 반비례하여 감소해 나간다는 것을 발견했습니다. 즉, 어떤 수 $x$ 근처에서 소수가 발견될 확률은 약 $\frac{1}{\ln x}$ 일 것이라고 예상한 것입니다.&lt;/p>
&lt;p>이것을 적분을 사용하여 표현한 것이 &lt;strong>로그 적분(Logarithmic integral)&lt;/strong> $\text{Li}(x)$ 입니다.&lt;/p>
$$ \text{Li}(x) = \int_{2}^{x} \frac{dt}{\ln t} $$
&lt;p>가우스의 예상은 훗날 1896년 자크 아다마르와 샤를 장 드 라 발레 푸생에 의해 독립적으로 증명되었고, **소수 정리(Prime Number Theorem, PNT)**로서 확립되었습니다.&lt;/p>
$$ \lim_{x \to \infty} \frac{\pi(x)}{\text{Li}(x)} = 1 $$
&lt;p>또는 근사적으로 다음과 같이 표현됩니다.&lt;/p>
$$ \pi(x) \sim \frac{x}{\ln x} $$
&lt;p>이 정리에 의해, 소수는 거시적으로 보면 매우 매끄럽고 예측 가능한 분포를 가지고 있다는 것을 알게 되었습니다. 하지만 미시적으로 보면 $\pi(x)$ 와 $\text{Li}(x)$ 사이에는 항상 &amp;lsquo;오차&amp;rsquo;, 즉 &amp;lsquo;요동(fluctuation)&amp;lsquo;이 존재합니다. 이 요동의 실체야말로 리만 가설이 밝혀내고자 하는 가장 큰 미스터리인 것입니다.&lt;/p>
&lt;hr>
&lt;h1 id="3-리만-제타-함수와-오일러-곱">3. 리만 제타 함수와 오일러 곱
&lt;/h1>&lt;p>소수의 분포를 분석하는 데 있어 가장 강력한 무기가 되는 것이 **리만 제타 함수(Riemann Zeta Function)**입니다. 원래는 레온하르트 오일러(Leonhard Euler)에 의해 실수 $s > 1$ 에 대해 정의된 무한급수였습니다.&lt;/p>
$$ \zeta(s) = \sum_{n=1}^\infty \frac{1}{n^s} = 1 + \frac{1}{2^s} + \frac{1}{3^s} + \frac{1}{4^s} + \dots $$
&lt;p>오일러의 가장 큰 업적 중 하나는, 이 무한급수가 모든 소수 $p$ 에 대한 무한곱으로 표현될 수 있음을 증명한 것입니다. 이것이 **오일러 곱(Euler Product Formula)**입니다.&lt;/p>
$$ \zeta(s) = \prod_{p \text{ prime}} \frac{1}{1 - p^{-s}} = \left( \frac{1}{1 - 2^{-s}} \right) \left( \frac{1}{1 - 3^{-s}} \right) \left( \frac{1}{1 - 5^{-s}} \right) \dots $$
&lt;p>증명의 직관적인 이해는 우변의 각 항을 등비급수로 전개하고 그것들을 곱하면, 산술의 기본 정리(모든 자연수는 소수의 곱으로 유일하게 표현된다)에 의해 좌변의 자연수의 역수 합이 완전히 재구성된다는 것입니다.&lt;/p>
&lt;p>&lt;strong>이 단 하나의 수식이 해석학(무한급수·연속함수)과 정수론(소수·이산적인 수)을 연결하는 다리가 되었습니다.&lt;/strong> 제타 함수를 조사하는 것은 소수의 분포를 조사하는 것과 같은 의미입니다.&lt;/p>
&lt;hr>
&lt;h1 id="4-해석적-연속과-복소평면으로의-확장">4. 해석적 연속과 복소평면으로의 확장
&lt;/h1>&lt;p>리만의 천재성은 오일러가 실수로만 생각했던 $\zeta(s)$ 의 변수 $s$ 를 **복소수 $s = \sigma + it$($\sigma$ 는 실수부, $t$ 는 허수부)**로 확장한 데 있습니다.&lt;/p>
&lt;p>원래의 무한급수는 $\sigma > 1$ 에서만 수렴하지만, 리만은 &amp;lsquo;해석적 연속(Analytic Continuation)&amp;lsquo;이라는 기법을 사용하여 $s = 1$ 의 극을 제외한 전체 복소평면상에서 $\zeta(s)$ 가 의미를 갖도록 정의를 확장했습니다.&lt;/p>
&lt;p>그는 더 나아가 제타 함수가 만족하는 아름다운 함수 방정식(Functional equation)을 이끌어냈습니다.&lt;/p>
$$ \zeta(s) = 2^s \pi^{s-1} \sin\left(\frac{\pi s}{2}\right) \Gamma(1-s) \zeta(1-s) $$
&lt;p>여기서 $\Gamma(x)$ 는 감마 함수입니다. 이 방정식에 의해 우반평면의 성질로부터 좌반평면의 성질을 알 수 있습니다.&lt;/p>
&lt;h3 id="영점zeros-of-the-zeta-function">영점(Zeros of the Zeta Function)
&lt;/h3>&lt;p>제타 함수의 값이 0이 되는 복소수 $s$ 를 &amp;lsquo;영점&amp;rsquo;이라고 부릅니다.
함수 방정식에서 $s$ 가 음의 짝수($-2, -4, -6, \dots$)일 때 $\sin(\pi s / 2)$ 가 0이 되므로, $\zeta(s) = 0$ 이 됩니다. 이것들은 **자명한 영점(Trivial zeros)**이라고 불립니다.&lt;/p>
&lt;p>그러나 소수의 분포에서 중요한 것은 그 외의 영점, 즉 $0 \le \sigma \le 1$ 인 &amp;lsquo;임계 영역(Critical strip)&amp;lsquo;에 존재하는 **비자명한 영점(Non-trivial zeros)**입니다.&lt;/p>
&lt;hr>
&lt;h1 id="5-리만-가설의-핵심과-명시-공식">5. 리만 가설의 핵심과 명시 공식
&lt;/h1>&lt;p>리만은 소수의 영점을 계산하고 어떤 놀라운 가설을 세웠습니다. 이것이 &lt;strong>리만 가설&lt;/strong>입니다.&lt;/p>
&lt;blockquote>
&lt;p>&lt;strong>리만 가설 (Riemann Hypothesis)&lt;/strong>
리만 제타 함수 $\zeta(s)$ 의 비자명한 영점은 모두 실수부가 $1/2$ 인 직선상($\text{Re}(s) = 1/2$)에 존재한다.&lt;/p>
&lt;/blockquote>
&lt;p>이 실수부가 1/2인 직선을 &amp;lsquo;임계선(Critical line)&amp;lsquo;이라고 부릅니다.&lt;/p>
&lt;div class="mermaid">graph TD
A["리만 제타 함수 ζ(s)"] --> B["해석적 연속을 통한 복소평면으로의 확장"]
B --> C["자명한 영점 (s = -2, -4, -6 ...)"]
B --> D["비자명한 영점 (0 &lt;= Re(s) &lt;= 1)"]
D --> E["리만 가설"]
E --> F["모든 비자명한 영점은 Re(s) = 1/2 상에 있다"]
F --> G["소수 분포의 오차항 극한 증명으로"]&lt;/div>
&lt;p>왜 리만 가설이 그토록 중요한 것일까요? 그것은 제타 함수의 영점이 소수의 분포를 &lt;strong>완전히&lt;/strong> 결정하고 있기 때문입니다.&lt;/p>
&lt;p>리만과 후대의 수학자 폰 망골트(von Mangoldt)는 소수의 분포를 정확하게 기술하는 &amp;lsquo;명시 공식(Explicit formula)&amp;lsquo;을 도출했습니다. 체비쇼프 함수 $\psi(x)$ 를 사용하면 다음과 같이 표현됩니다.&lt;/p>
$$ \psi(x) = x - \sum_{\rho} \frac{x^\rho}{\rho} - \ln(2\pi) - \frac{1}{2}\ln(1 - x^{-2}) $$
&lt;p>여기서 $\rho$ 는 제타 함수의 모든 비자명한 영점을 지나는 합입니다.
주항은 $x$ (이것은 소수 정리에 대응)이며, 거기서 영점 $\rho$ 에 의존하는 파동 같은 항을 더하고 뺌으로써 소수의 계단형태의 정확한 분포가 복원되는 것입니다. 비자명한 영점은 소수 분포의 &amp;lsquo;주파수(파동)&amp;lsquo;를 나타낸다고 할 수 있습니다.&lt;/p>
&lt;p>만약 리만 가설이 맞고, 모든 비자명한 영점 $\rho$ 의 실수부가 정확히 $1/2$ 이라면, 소수 정리의 오차항은 이론상 상상할 수 있는 최소 범위 내에 수렴하게 됩니다.&lt;/p>
$$ |\pi(x) - \text{Li}(x)| \le \frac{1}{8\pi} \sqrt{x} \ln x \quad \text{for} \quad x \ge 2657 $$
&lt;p>즉, &lt;strong>리만 가설이 참이라면, 소수는 우리가 상상할 수 있는 한 가장 &amp;lsquo;규칙적이고 아름답게&amp;rsquo; 분포하고 있다&lt;/strong>는 것이 증명되는 것입니다.&lt;/p>
&lt;hr>
&lt;h1 id="6-현대-암호-기술과-소수의-불가분한-관계">6. 현대 암호 기술과 소수의 불가분한 관계
&lt;/h1>&lt;p>여기까지는 심오한 순수 수학의 세계였지만, 이 소수의 성질은 현대의 디지털 사회를 근본적으로 지탱하고 있습니다. 그 대표적인 예가 &lt;strong>RSA 암호&lt;/strong>를 비롯한 공개키 암호 방식입니다.&lt;/p>
&lt;p>인터넷에서의 신용카드 결제, 비밀번호 전송, 블록체인의 전자 서명 등 모든 통신의 안전성은 &amp;lsquo;소수&amp;rsquo;에 의존하고 있습니다.&lt;/p>
&lt;h3 id="rsa-암호의-원리">RSA 암호의 원리
&lt;/h3>&lt;p>RSA 암호의 안전성은 &amp;lsquo;자릿수가 큰 합성수의 소인수분해는 매우 어렵다&amp;rsquo;는 수학적 사실(소인수분해 문제)에 기반하고 있습니다.&lt;/p>
&lt;ol>
&lt;li>
&lt;p>&lt;strong>키 생성&lt;/strong>:
거대한 소수 $p$ 와 $q$ (예를 들어 각각 2048비트)를 무작위로 선택합니다.
이들을 곱하여 $N = p \times q$ 를 계산합니다. 이 $N$ 이 공개키의 일부가 됩니다.
오일러의 파이 함수 $\phi(N) = (p-1)(q-1)$ 을 사용하여 개인키 $d$ 를 생성합니다.&lt;/p>
$$ e \times d \equiv 1 \pmod{\phi(N)} $$
&lt;/li>
&lt;li>
&lt;p>&lt;strong>암호화와 복호화&lt;/strong>:
평문 $M$ 은 공개키 $e, N$ 을 사용하여 암호문 $C$ 로 변환됩니다.
&lt;/p>
$$ C \equiv M^e \pmod{N} $$
&lt;p>
개인키 $d$ 를 가진 사람만이 복호화할 수 있습니다.
&lt;/p>
$$ M \equiv C^d \pmod{N} $$
&lt;/li>
&lt;/ol>
&lt;div class="mermaid">graph LR
A["평문 (Plaintext)"] --> B["공개키(e, N)로 암호화"]
B --> C["암호문 (Ciphertext)"]
C --> D["개인키(d)로 복호화"]
D --> E["원래의 평문"]
F["공격자 (Attacker)"] -- "N의 소인수분해를 시도" --> C
F -.-> G["p와 q를 모르면 d는 계산 불가"]&lt;/div>
&lt;p>RSA 암호를 깨기 위해서는 거대한 $N$ 에서 원래의 소수 $p$ 와 $q$ 를 찾아내야(소인수분해해야) 합니다. 현재 주류인 알고리즘(일반 수체 체: GNFS 등)을 사용하더라도, 수백 자리의 수를 소인수분해하려면 슈퍼컴퓨터를 사용해도 우주의 나이를 훨씬 초과하는 시간이 걸리는 것으로 알려져 있습니다.&lt;/p>
&lt;hr>
&lt;h1 id="7-리만-가설이-암호-기술에-미치는-영향">7. 리만 가설이 암호 기술에 미치는 영향
&lt;/h1>&lt;p>그렇다면 순수 수학의 정점에 있는 &amp;lsquo;리만 가설&amp;rsquo;과 &amp;lsquo;암호 기술&amp;rsquo;은 어떻게 교차하는 것일까요?&lt;/p>
&lt;h3 id="71-소수-생성-알고리즘소수-판별과-일반화된-리만-가설grh">7.1. 소수 생성 알고리즘(소수 판별)과 일반화된 리만 가설(GRH)
&lt;/h3>&lt;p>RSA 암호를 운용하기 위해서는 먼저 거대한 소수 $p$ 와 $q$ 를 생성해야 합니다. 하지만 &amp;lsquo;어떤 수가 소수인지&amp;rsquo;를 확실하고 빠르게 판별하는 것은 쉽지 않습니다.&lt;/p>
&lt;p>현재 실용적으로 사용되고 있는 것은 **밀러-라빈 소수 판별법(Miller-Rabin primality test)**이라는 확률적 알고리즘입니다. 이 알고리즘은 매우 빠르지만, 아주 낮은 확률로 합성수를 소수라고 잘못 판별하는 &amp;lsquo;유사소수&amp;rsquo;의 위험이 있습니다.&lt;/p>
&lt;p>하지만 리만 가설을 디리클레 L-함수로 확장한 **&amp;lsquo;일반화된 리만 가설(Generalized Riemann Hypothesis, GRH)&amp;rsquo;**이 참이라고 가정하면 이야기는 극적으로 달라집니다.
GRH가 참이라면, 밀러-라빈 판별법에서의 테스트 횟수 상한이 수학적으로 보장되어 확률적 알고리즘에서 &lt;strong>&amp;lsquo;결정론적 다항 시간 알고리즘&amp;rsquo;으로 승화&lt;/strong>하는 것입니다(이는 AKS 소수 판별법이 발견되기 이전부터 알려져 있던 중대한 사실이었습니다).&lt;/p>
&lt;p>즉, 리만 가설(및 그 확장)은 &amp;lsquo;거대한 소수를 절대적인 확신을 가지고 빠르게 생성할 수 있는가&amp;rsquo;라는 암호의 기반 생성에 직접적인 보증을 부여하는 역할을 가지고 있습니다.&lt;/p>
&lt;h3 id="72-소인수분해-알고리즘과의-관계">7.2. 소인수분해 알고리즘과의 관계
&lt;/h3>&lt;p>암호를 해독하는 쪽의 알고리즘(일반 수체 체 등)의 계산 복잡도를 평가할 때도 소수의 분포에 관한 지식이 필수적입니다. 소인수분해 알고리즘의 대부분은 &amp;lsquo;매끄러운 수(Smooth numbers: 작은 소인수만을 가지는 수)&amp;lsquo;의 분포에 의존하고 있습니다.&lt;/p>
&lt;p>매끄러운 수가 어느 정도의 빈도로 나타나는지를 엄밀하게 평가하기 위해서는 소수의 분포에 관한 깊은 이해가 필요하며, 여기서도 제타 함수나 리만 가설에 직결되는 해석적 정수론의 기법이 구사되고 있습니다. 리만 가설이 증명되어 소수 분포의 오차가 완전히 결정된다면, 소인수분해 알고리즘의 성능 한계도 더욱 정확하게 파악할 수 있게 됩니다.&lt;/p>
&lt;hr>
&lt;h1 id="8-만약-리만-가설이-증명된다면-암호는-뚫릴-것인가">8. 만약 리만 가설이 증명된다면, 암호는 뚫릴 것인가?
&lt;/h1>&lt;p>도시 괴담처럼 &amp;lsquo;리만 가설이 풀리면 RSA 암호는 순식간에 붕괴한다&amp;rsquo;고 이야기되는 경우가 있지만, &lt;strong>이는 수학적으로 부정확합니다&lt;/strong>.&lt;/p>
&lt;p>리만 가설의 증명 자체가 즉각적으로 소인수분해를 극적으로 고속화하는 마법의 알고리즘을 만들어내는 것은 아닙니다. 리만 가설은 어디까지나 소수의 &amp;lsquo;거시적인 분포의 규칙성&amp;rsquo;에 대한 정리이며, 개별 수 $N$ 이 어떤 소수로 나누어떨어지는지(국소적인 성질)를 직접 가르쳐 주는 것은 아니기 때문입니다.&lt;/p>
&lt;p>하지만 그 영향이 제로는 아닙니다.
리만 가설이 증명되는 과정에서 &lt;strong>&amp;lsquo;새로운 수학적 도구&amp;rsquo;나 &amp;lsquo;미지의 해석 기법&amp;rsquo;이 발견될 가능성&lt;/strong>이 매우 높기 때문입니다. 역사를 보더라도, 페르마의 마지막 정리나 푸앵카레 추측이 증명되었을 때 그 과정에서 개발된 새로운 이론이 수학 전체를 크게 도약시켰습니다.&lt;/p>
&lt;p>만약 리만 제타 함수의 영점의 성질을 완전히 조작할 수 있는 미지의 대수 기하학적 기법이나, 비가환 기하의 기법이 확립된다면, 그것이 결과적으로 소인수분해의 획기적인 알고리즘(예를 들어, 계산 복잡도를 다항 시간으로 줄이는 고전 알고리즘)의 발견으로 이어질 가능성은 부정할 수 없습니다. 그런 의미에서 암호학자들은 리만 가설의 동향에서 결코 눈을 뗄 수 없는 것입니다.&lt;/p>
&lt;h3 id="양자-컴퓨터와-쇼어의-알고리즘">양자 컴퓨터와 쇼어의 알고리즘
&lt;/h3>&lt;p>암호 기술에 있어 보다 직접적이고 현실적인 위협은 리만 가설의 증명이 아니라 &lt;strong>양자 컴퓨터&lt;/strong>입니다. 1994년 피터 쇼어(Peter Shor)가 발표한 &amp;lsquo;쇼어의 알고리즘&amp;rsquo;은 충분한 성능을 가진 양자 컴퓨터가 있다면 소인수분해를 다항 시간에 풀 수 있음을 증명했습니다. 이로 인해 RSA 암호나 타원곡선암호는 근본적으로 뚫리게 됩니다.&lt;/p>
&lt;p>현재 전 세계적으로 양자 컴퓨터로도 해독할 수 없는 &amp;lsquo;양자 내성 암호(Post-Quantum Cryptography, PQC)&amp;lsquo;로의 전환(격자 암호 등)이 추진되고 있습니다. 소수에 의존한 암호 기술은 어떤 의미에서 황금기를 끝내려 하고 있는지도 모르지만, 소수 그 자체의 수학적 가치가 사라지는 일은 영원히 없을 것입니다.&lt;/p>
&lt;hr>
&lt;h1 id="9-결론-수학의-추상성과-현실-사회의-교차점">9. 결론: 수학의 추상성과 현실 사회의 교차점
&lt;/h1>&lt;div class="mermaid">graph TD
A["순수 수학의 탐구"] --> B["리만 가설의 규명"]
B --> C["소수 분포의 완전한 이해"]
C --> D["정수론·대수 기하학의 비약적 발전"]
D -.-> E["새로운 소인수분해 알고리즘의 가능성"]
E -.-> F["암호 기술의 안전성 평가 갱신"]
A --> G["응용 수학·컴퓨터 과학"]
G --> H["소수 판별·암호 생성의 효율화"]
H --> F&lt;/div>
&lt;p>고대 그리스부터 이어져 온 소수에 대한 끝없는 탐구는 리만이라는 천재에 의해 복소평면상의 아름다운 심포니(제타 함수의 영점)로 승화되었습니다. 그리고 놀랍게도 그 순수무구한 수학의 결정체가 수세기의 시간을 거쳐 인터넷 사회의 안전을 담보하는 최강의 방패로서 응용되고 있습니다.&lt;/p>
&lt;p>리만 가설은 수학이 가진 &amp;lsquo;추상적인 아름다움&amp;rsquo;과 &amp;lsquo;물리 세계·현실 사회에 대한 경이로운 적용력&amp;rsquo;을 동시에 상징하는 존재입니다.&lt;/p>
&lt;p>아직 아무도 정상에 도달하지 못한 이 거대한 수학의 산이 언젠가 정복될 때, 우리는 소수라는 우주의 진리를 완전히 이해함과 동시에 정보화 사회의 기반에 대해 새로운 시각을 갖게 될 것입니다. 암호 기술을 배우는 것은 그대로 인류 지혜의 역사를 따라가는 여정이기도 한 것입니다.&lt;/p></description></item><item><title>【수학 해설】 고등학생도 이해할 수 있는 RSA 암호의 원리</title><link>http://kenji.blog/ko/p/rsa-encryption-math-explained-for-beginners/</link><pubDate>Fri, 11 Sep 2026 13:00:00 +0900</pubDate><guid>http://kenji.blog/ko/p/rsa-encryption-math-explained-for-beginners/</guid><description>&lt;img src="http://kenji.blog/p/rsa-encryption-math-explained-for-beginners/img/eyecatch.jpg" alt="Featured image of post 【수학 해설】 고등학생도 이해할 수 있는 RSA 암호의 원리" />&lt;p>인터넷 사회의 안전을 근본적으로 지탱하고 있는 기술 중 하나가 &amp;lsquo;RSA 암호&amp;rsquo;입니다. 온라인 쇼핑에서의 신용카드 결제, 친구와의 SNS 대화, 회사의 기밀 정보 송수신 등 우리가 매일 무심코 사용하는 통신의 대부분은 이 RSA 암호나 그 후속 기술에 의해 보호받고 있습니다.&lt;/p>
&lt;p>하지만 &amp;lsquo;암호&amp;rsquo;라고 하면 스파이 영화에 나올 법한 복잡한 암호기나 일부 천재만이 이해할 수 있는 초고도 수학을 상상할지도 모릅니다. 현대 암호 이론이 고도화된 수학을 바탕으로 하는 것은 사실이지만, &lt;strong>RSA 암호의 근본적인 원리는 고등학교에서 배우는 수학(정수의 성질, 소수, 합동식 등) 지식만 있다면 충분히 이해할 수 있는&lt;/strong> 것입니다.&lt;/p>
&lt;p>이 글에서는 고등학교 수학 지식을 출발점으로 삼아, RSA 암호가 어떤 수학적 원리로 작동하는지, 왜 해독하기 어려운지를 단계별로 철저하게 해설합니다. 수학을 조금 어려워하는 분들도 이해할 수 있도록 구체적인 예를 곁들여 자세히 설명하겠습니다.&lt;/p>
&lt;hr>
&lt;h2 id="1-공통키-암호와-공개키-암호">1. 공통키 암호와 공개키 암호
&lt;/h2>&lt;p>RSA 암호의 수학적인 원리에 들어가기 전에, 먼저 암호의 기본적인 개념부터 정리해 보겠습니다. 암호 방식은 크게 나누어 &amp;lsquo;공통키 암호(대칭키 암호)&amp;lsquo;와 &amp;lsquo;공개키 암호&amp;rsquo; 두 가지로 나뉩니다.&lt;/p>
&lt;h3 id="11-공통키-암호-방식의-한계">1.1 공통키 암호 방식의 한계
&lt;/h3>&lt;p>예전부터 사용되어 온 암호의 대부분은 &amp;lsquo;공통키 암호 방식&amp;rsquo;이라고 불리는 것입니다. 이것은 &lt;strong>&amp;lsquo;암호화(메시지를 비밀 암호문으로 변환하는 것)&amp;lsquo;와 &amp;lsquo;복호화(암호문을 원래 메시지로 되돌리는 것)&amp;lsquo;에 같은 키를 사용하는&lt;/strong> 방식입니다.&lt;/p>
&lt;p>예를 들어, 앨리스가 밥에게 비밀 편지를 보낸다고 가정해 보겠습니다. 앨리스는 자물쇠(공통키)를 사용하여 상자에 편지를 넣고 잠급니다. 밥이 그 상자를 열기 위해서는 앨리스가 사용한 것과 똑같은 키를 가지고 있어야 합니다.&lt;/p>
&lt;p>이 방식에는 큰 문제가 있습니다. 바로 &amp;lsquo;키 분배 문제&amp;rsquo;입니다. 멀리 떨어져 있는 앨리스와 밥이 처음 통신할 경우, 어떻게 도청당하지 않고 키를 공유할 수 있을까요? 만약 키를 우편으로 보내는 도중에 제3자에게 도난당한다면, 그 이후의 암호 통신은 모두 내용이 노출되고 맙니다.&lt;/p>
&lt;h3 id="12-획기적인-발명-공개키-암호-방식">1.2 획기적인 발명 &amp;lsquo;공개키 암호 방식&amp;rsquo;
&lt;/h3>&lt;p>이 키 분배 문제를 해결하기 위해 고안된 것이 &amp;lsquo;공개키 암호 방식&amp;rsquo;입니다. RSA 암호도 이것의 일종입니다.&lt;/p>
&lt;p>공개키 암호 방식에서는 &lt;strong>&amp;lsquo;암호화하기 위한 키(공개키)&amp;lsquo;와 &amp;lsquo;복호화하기 위한 키(비밀키)&amp;lsquo;라는 서로 다른 두 개의 키&lt;/strong>를 사용합니다.&lt;/p>
&lt;ol>
&lt;li>수신자인 밥은 &amp;lsquo;공개키&amp;rsquo;와 &amp;lsquo;비밀키&amp;rsquo; 쌍을 생성합니다.&lt;/li>
&lt;li>밥은 &amp;lsquo;공개키&amp;rsquo;를 전 세계에 공개합니다 (누가 가져도 상관없습니다).&lt;/li>
&lt;li>송신자인 앨리스는 밥의 &amp;lsquo;공개키&amp;rsquo;를 사용하여 메시지를 암호화하고 전송합니다.&lt;/li>
&lt;li>암호화된 메시지는 밥만이 가지고 있는 &amp;lsquo;비밀키&amp;rsquo;로만 복호화할 수 있습니다.&lt;/li>
&lt;/ol>
&lt;p>이것을 자물쇠에 비유하자면, 밥은 &amp;lsquo;열려 있는 상태의 자물쇠(공개키)&amp;lsquo;를 많이 만들어서 전 세계에 뿌립니다. 앨리스는 밥에게 보낼 메시지를 상자에 넣고, 주운 밥의 자물쇠로 찰칵하고 잠급니다. 자물쇠는 한 번 닫히면 밥이 가지고 있는 &amp;lsquo;마스터키(비밀키)&amp;lsquo;로만 열 수 있습니다. 중간에 누군가 상자를 훔쳐도 마스터키가 없기 때문에 열 수 없는 것입니다.&lt;/p>
&lt;div class="mermaid">graph TD
A["앨리스 (송신자)"] --> B["평문 (메시지)"]
B --> C["암호화 처리"]
D["밥의 공개키 (누구나 획득 가능)"] --> C
C --> E["인터넷을 통해 전송: 암호문"]
E --> F["복호화 처리"]
G["밥의 비밀키 (밥만 소유)"] --> F
F --> H["복원된 평문 (메시지)"]
H --> I["밥 (수신자)"]&lt;/div>
&lt;p>이 획기적인 시스템을 구현하기 위해서는 &amp;lsquo;공개키로 쉽게 암호화할 수 있지만, 비밀키가 없으면 절대 복호화할 수 없다&amp;rsquo;는 일종의 **&amp;lsquo;일방향 함수(일방통행인 수학적 퍼즐)&amp;rsquo;**가 필요합니다. 그 퍼즐의 부품으로 주목받은 것이 바로 우리가 잘 아는 &amp;lsquo;소수&amp;rsquo;였습니다.&lt;/p>
&lt;hr>
&lt;h2 id="2-rsa-암호를-지탱하는-수학적-기초-1-소수와-소인수분해">2. RSA 암호를 지탱하는 수학적 기초 1: 소수와 소인수분해
&lt;/h2>&lt;p>RSA 암호의 안전성은 **&amp;lsquo;거대한 수의 소인수분해는 매우 어렵다&amp;rsquo;**는 수학적 사실에 바탕을 두고 있습니다.&lt;/p>
&lt;h3 id="21-소수란">2.1 소수란
&lt;/h3>&lt;p>소수란 &amp;lsquo;1과 자기 자신으로밖에 나누어떨어지지 않는, 1보다 큰 자연수&amp;rsquo;를 말합니다.
예: $2, 3, 5, 7, 11, 13, 17, 19, 23...$&lt;/p>
&lt;p>소수는 모든 정수의 &amp;lsquo;원자&amp;rsquo;와 같은 것입니다. 어떤 자연수라도 소수들의 곱셈 형태로 분해할 수 있습니다. 이것을 &lt;strong>소인수분해&lt;/strong>라고 부릅니다. 예를 들어 $60 = 2^2 \times 3 \times 5$ 처럼, 순서를 무시하면 단 한 가지로 소인수분해할 수 있다는 것은 &amp;lsquo;산술의 기본 정리&amp;rsquo;로 알려져 있습니다.&lt;/p>
&lt;h3 id="22-소인수분해의-어려움-일방향-함수">2.2 소인수분해의 어려움 (일방향 함수)
&lt;/h3>&lt;p>여기서 중요한 것은 **&amp;lsquo;곱셈은 쉽지만 소인수분해는 어렵다&amp;rsquo;**는 비대칭성입니다.&lt;/p>
&lt;p>예를 들어, 다음 두 소수의 곱셈을 암산으로 계산해 보세요.
$11 \times 13 = ?$
이것은 쉽네요. 정답은 $143$입니다.&lt;/p>
&lt;p>그럼, 다음 수는 어떨까요?
$323$을 소인수분해 해 보세요.
어떠신가요? 시간이 조금 걸릴 것입니다. (정답은 $17 \times 19$입니다).&lt;/p>
&lt;p>수가 작으면 사람도 어떻게든 계산할 수 있지만, 수가 커지면 컴퓨터를 사용해도 계산이 폭발적으로 어려워집니다. 현재 주류인 RSA 암호에서는 2048비트(10진수로 약 600자리)라는 터무니없이 거대한 소수 $p$와 $q$를 곱한 수 $N = p \times q$를 사용합니다.&lt;/p>
&lt;p>거대한 두 소수 $p$와 $q$가 주어졌을 때, $N$을 계산하는 것은 컴퓨터에게는 순식간(밀리초 이하)입니다. 하지만 반대로 $N$만 주어지고 원래의 $p$와 $q$를 찾아내는 것은 현재 가장 빠른 슈퍼컴퓨터를 수조 년 돌려도 풀지 못할 정도로 오랜 시간이 걸립니다.&lt;/p>
&lt;p>이 **&amp;lsquo;계산의 비대칭성(한쪽 방향은 쉽지만 역방향은 어려움)&amp;rsquo;**이 공개키와 비밀키의 관계성을 만들어내는 토대가 됩니다.&lt;/p>
&lt;hr>
&lt;h2 id="3-rsa-암호를-지탱하는-수학적-기초-2-합동식-모듈로-연산">3. RSA 암호를 지탱하는 수학적 기초 2: 합동식 (모듈로 연산)
&lt;/h2>&lt;p>RSA 암호의 계산은 우리가 평소 사용하는 무한히 숫자가 커지는 덧셈이나 곱셈이 아니라, 어떤 수로 나눈 &amp;lsquo;나머지&amp;rsquo;의 세계에서 이루어집니다. 이것을 **합동식(모듈로 연산)**이라고 부릅니다.&lt;/p>
&lt;h3 id="31-시계의-수학">3.1 시계의 수학
&lt;/h3>&lt;p>모듈로 연산은 종종 &amp;lsquo;시계의 수학&amp;rsquo;에 비유됩니다. 현재 10시라고 할 때, 그로부터 5시간 후는 몇 시일까요? $10 + 5 = 15$시이지만, 일반적인 12시간 시계에서는 &amp;lsquo;3시&amp;rsquo;라고 대답합니다. 이것은 15를 12로 나눈 나머지가 3이기 때문입니다.&lt;/p>
&lt;p>수학의 세계에서는 이것을 다음과 같이 표기합니다.
&lt;/p>
$$ 15 \equiv 3 \pmod{12} $$
&lt;p>
&amp;ldquo;15와 3은 12를 법으로 하여 합동이다(12로 나눈 나머지가 같다)&amp;ldquo;라고 읽습니다.&lt;/p>
&lt;h3 id="32-합동식의-기본적인-성질">3.2 합동식의 기본적인 성질
&lt;/h3>&lt;p>합동식에는 등식($=$)과 매우 비슷한 유용한 성질이 있습니다. 법(나누는 수)을 $N$이라고 하겠습니다.
$a \equiv b \pmod N$ 이고 $c \equiv d \pmod N$ 일 때, 다음이 성립합니다.&lt;/p>
&lt;ol>
&lt;li>&lt;strong>덧셈:&lt;/strong> $a + c \equiv b + d \pmod N$&lt;/li>
&lt;li>&lt;strong>뺄셈:&lt;/strong> $a - c \equiv b - d \pmod N$&lt;/li>
&lt;li>&lt;strong>곱셈:&lt;/strong> $a \times c \equiv b \times d \pmod N$&lt;/li>
&lt;li>&lt;strong>거듭제곱:&lt;/strong> $a^k \equiv b^k \pmod N$ ($k$는 자연수)&lt;/li>
&lt;/ol>
&lt;p>특히 중요한 것은 &amp;lsquo;거듭제곱&amp;rsquo;의 성질입니다. 이것은 **&amp;lsquo;나머지의 거듭제곱은 거듭제곱의 나머지와 같다&amp;rsquo;**는 것을 의미합니다.
예를 들어, $7^{100}$을 $5$로 나눈 나머지를 구하고 싶다고 합시다. 정직하게 $7$을 100번 곱한 뒤 $5$로 나누는 것은 힘들지만, 합동식의 성질을 사용하면 $7 \equiv 2 \pmod 5$ 이므로 $7^{100} \equiv 2^{100} \pmod 5$ 가 되어 계산을 비약적으로 단순하게 만들 수 있습니다. 암호의 세계에서는 매우 큰 수의 거듭제곱을 다루기 때문에 이 성질이 필수적입니다.&lt;/p>
&lt;hr>
&lt;h2 id="4-rsa-암호를-지탱하는-수학적-기초-3-오일러-함수와-오일러의-정리">4. RSA 암호를 지탱하는 수학적 기초 3: 오일러 함수와 오일러의 정리
&lt;/h2>&lt;p>여기서부터가 RSA 암호의 핵심이 되는 마법의 수학입니다. &amp;lsquo;페르마의 소정리&amp;rsquo;를 일반화한 &amp;lsquo;오일러의 정리&amp;rsquo;가 등장합니다.&lt;/p>
&lt;h3 id="41-오일러의-토션트-함수-phin">4.1 오일러의 토션트 함수 $\phi(N)$
&lt;/h3>&lt;p>오일러의 토션트 함수($\phi$ 함수)는 어떤 자연수 $N$에 대하여, **&amp;lsquo;1부터 $N$까지의 자연수 중 $N$과 서로소(최대공약수가 1)인 수의 개수&amp;rsquo;**를 반환하는 함수입니다.&lt;/p>
&lt;p>몇 가지 예를 살펴보겠습니다.&lt;/p>
&lt;ul>
&lt;li>$\phi(5)$: 1, 2, 3, 4, 5 중에서 5와 서로소인 것은 1, 2, 3, 4의 4개. 따라서 $\phi(5) = 4$.&lt;/li>
&lt;li>$\phi(6)$: 1, 2, 3, 4, 5, 6 중에서 6과 서로소인 것은 1, 5의 2개. 따라서 $\phi(6) = 2$.&lt;/li>
&lt;/ul>
&lt;p>&lt;strong>【소수인 경우의 특별한 성질】&lt;/strong>
$p$가 소수인 경우, 1부터 $p-1$까지의 모든 수가 $p$와 서로소가 됩니다. 따라서,
&lt;/p>
$$ \phi(p) = p - 1 $$
&lt;p>
이 됩니다.&lt;/p>
&lt;p>&lt;strong>【두 소수의 곱인 경우의 특별한 성질】&lt;/strong>
서로 다른 두 소수 $p$와 $q$에 대해 $N = p \times q$ 라고 할 때, $\phi(N)$은 다음 계산으로 쉽게 구할 수 있습니다.
&lt;/p>
$$ \phi(N) = \phi(p) \times \phi(q) = (p - 1)(q - 1) $$
&lt;p>
이 성질이 RSA 암호의 &amp;lsquo;비밀 뒷문(트랩도어)&amp;rsquo; 역할을 합니다. $p$와 $q$를 아는 사람(키 생성자)은 $\phi(N)$을 순식간에 계산할 수 있지만, $N$밖에 모르는 제3자는 $N$을 소인수분해하지 않는 한 $\phi(N)$을 구할 수 없는 것입니다.&lt;/p>
&lt;h3 id="42-오일러의-정리">4.2 오일러의 정리
&lt;/h3>&lt;p>레온하르트 오일러는 이 $\phi(N)$을 사용하여 다음과 같은 아름다운 정리를 증명했습니다.&lt;/p>
&lt;p>&lt;strong>오일러의 정리:&lt;/strong>
정수 $a$와 $N$이 서로소일 때, 다음 합동식이 성립한다.
&lt;/p>
$$ a^{\phi(N)} \equiv 1 \pmod N $$
&lt;p>이것은 &amp;ldquo;어떤 수 $a$를 $\phi(N)$번 곱하여 $N$으로 나누면 나머지가 반드시 $1$이 된다&amp;quot;는 놀라운 성질입니다. ($N$이 소수 $p$인 경우는 $a^{p-1} \equiv 1 \pmod p$ 가 되며, 페르마의 소정리라고 부릅니다).&lt;/p>
&lt;p>이 오일러의 정리를 변형해 보겠습니다. 양변에 $a$를 한 번 더 곱합니다.
&lt;/p>
$$ a^{\phi(N) + 1} \equiv a \pmod N $$
&lt;p>나아가 임의의 정수 $k$에 대하여 $a^{k \cdot \phi(N)}$ 도 $1^k = 1$이 되므로, 다음 식이 성립합니다.
&lt;/p>
$$ a^{k \cdot \phi(N) + 1} \equiv a \pmod N $$
&lt;p>이 식이야말로 RSA 암호의 **&amp;lsquo;암호화한 뒤 복호화하면 원래대로 돌아온다&amp;rsquo;**는 마법을 성립시키는 근본 원리입니다.&lt;/p>
&lt;hr>
&lt;h2 id="5-rsa-암호-알고리즘-키-생성암호화복호화-단계">5. RSA 암호 알고리즘: 키 생성・암호화・복호화 단계
&lt;/h2>&lt;p>기초 지식이 갖춰졌으니, 이제 드디어 RSA 암호의 구체적인 절차를 살펴보겠습니다. RSA 암호는 크게 &amp;lsquo;1. 키 생성&amp;rsquo;, &amp;lsquo;2. 암호화&amp;rsquo;, &amp;lsquo;3. 복호화&amp;rsquo;의 세 단계로 나뉩니다.&lt;/p>
&lt;div class="mermaid">flowchart TD
A1["1. 소수 p, q를 선택한다"] --> A2["N = p × q 를 계산한다"]
A1 --> A3["φ(N) = (p-1)(q-1) 을 계산한다"]
A3 --> A4["φ(N)과 서로소인 e를 선택한다"]
A3 --> A5["e × d ≡ 1 (mod φ(N)) 이 되는 d를 계산한다"]
A2 --> A6["공개키 (N, e)"]
A4 --> A6
A5 --> A7["비밀키 d"]
B1["2. 평문 메시지 M"] --> B2["C ≡ M^e (mod N) 을 계산한다"]
A6 -.-> B2
B2 --> B3["암호문 C를 전송한다"]
B3 --> C1["3. 수신한 암호문 C"]
C1 --> C2["M ≡ C^d (mod N) 을 계산한다"]
A7 -.-> C2
C2 --> C3["원래의 평문 메시지 M을 얻는다"]&lt;/div>
&lt;h3 id="51-키-생성-key-generation">5.1 키 생성 (Key Generation)
&lt;/h3>&lt;p>수신자인 밥은 자신을 위한 &amp;lsquo;공개키&amp;rsquo;와 &amp;lsquo;비밀키&amp;rsquo;를 생성합니다.&lt;/p>
&lt;ol>
&lt;li>&lt;strong>소수 선택:&lt;/strong> 임의의 두 큰 소수 $p$와 $q$를 선택합니다.&lt;/li>
&lt;li>&lt;strong>법 $N$ 계산:&lt;/strong> $N = p \times q$ 를 계산합니다. 이 $N$은 공개됩니다.&lt;/li>
&lt;li>&lt;strong>$\phi(N)$ 계산:&lt;/strong> 오일러 함수 $\phi(N) = (p - 1)(q - 1)$ 을 계산합니다. 이것은 밥만의 비밀 숫자입니다.&lt;/li>
&lt;li>&lt;strong>공개키 $e$ 선택:&lt;/strong> $1 &lt; e &lt; \phi(N)$ 이면서 $\phi(N)$과 서로소인 정수 $e$를 선택합니다.&lt;/li>
&lt;li>&lt;strong>비밀키 $d$ 계산:&lt;/strong> 다음 조건을 만족하는 정수 $d$를 찾습니다.
$$ e \times d \equiv 1 \pmod{\phi(N)} $$
이것은 즉 &amp;lsquo;$e \times d$를 $\phi(N)$으로 나눈 나머지가 $1$이 되는 수 $d$&amp;lsquo;를 뜻합니다.&lt;/li>
&lt;/ol>
&lt;p>이것으로 키 준비는 완료되었습니다.&lt;/p>
&lt;ul>
&lt;li>&lt;strong>공개키:&lt;/strong> $(N, e)$ 쌍. 전 세계에 공개합니다.&lt;/li>
&lt;li>&lt;strong>비밀키:&lt;/strong> $d$. 절대 누구에게도 알려주지 않습니다.&lt;/li>
&lt;/ul>
&lt;h3 id="52-암호화-encryption">5.2 암호화 (Encryption)
&lt;/h3>&lt;p>앨리스는 밥에게 비밀 메시지 $M$을 보내고 싶습니다. ($M$은 문자를 수치화한 것으로 $0 \le M &lt; N$ 이라고 가정합니다). 앨리스는 밥의 공개키 $(N, e)$ 를 사용하여 다음과 같이 계산합니다.&lt;/p>
$$ C \equiv M^e \pmod N $$
&lt;p>&amp;lsquo;메시지 $M$을 $e$제곱하고, $N$으로 나눈 나머지 $C$&amp;lsquo;를 계산합니다. 이 $C$가 암호문입니다.&lt;/p>
&lt;h3 id="53-복호화-decryption">5.3 복호화 (Decryption)
&lt;/h3>&lt;p>밥은 암호문 $C$를 받습니다. 밥은 비밀키 $d$를 사용하여 다음과 같이 계산합니다.&lt;/p>
$$ M \equiv C^d \pmod N $$
&lt;p>&amp;lsquo;암호문 $C$를 $d$제곱하고, $N$으로 나눈 나머지&amp;rsquo;를 계산하면, 놀랍게도 원래의 메시지 $M$이 복원되는 것입니다!&lt;/p>
&lt;hr>
&lt;h2 id="6-왜-복호화하면-원래대로-돌아가는가-수학적-증명">6. 왜 복호화하면 원래대로 돌아가는가? (수학적 증명)
&lt;/h2>&lt;p>&amp;ldquo;$C$를 $d$제곱하는 것만으로 어떻게 원래의 $M$으로 돌아가지?&amp;ldquo;라고 의문이 들 수 있습니다. 여기서 앞서 다룬 &amp;lsquo;오일러의 정리&amp;rsquo;가 위력을 발휘합니다.&lt;/p>
&lt;p>복호화 계산식 $C^d \pmod N$ 에 암호화 식 $C = M^e$ 를 대입해 보겠습니다.
&lt;/p>
$$ C^d \equiv (M^e)^d \equiv M^{ed} \pmod N $$
&lt;p>여기서 키 생성의 5단계를 떠올려 보세요. 밥은 $d$를 만들 때, $e \times d \equiv 1 \pmod{\phi(N)}$ 이 되도록 선택했습니다. 이것은 &amp;lsquo;$ed$는 $\phi(N)$의 배수에 $1$을 더한 수이다&amp;rsquo;라는 것을 의미합니다. 정수 $k$를 사용하여 다음과 같이 쓸 수 있습니다.
&lt;/p>
$$ ed = k \cdot \phi(N) + 1 $$
&lt;p>이것을 지수 부분에 대입하고, 지수 법칙을 사용하여 분해합니다.
&lt;/p>
$$ M^{ed} = M^{k \cdot \phi(N) + 1} = M^{k \cdot \phi(N)} \times M^1 = (M^{\phi(N)})^k \times M $$
&lt;p>여기서 메시지 $M$과 $N$이 서로소라고 가정하면, &lt;strong>오일러의 정리&lt;/strong>에 의해 $M^{\phi(N)} \equiv 1 \pmod N$ 이 됩니다.
&lt;/p>
$$ (M^{\phi(N)})^k \times M \equiv 1^k \times M \equiv M \pmod N $$
&lt;p>따라서 멋지게 다음 식이 성립하게 됩니다.
&lt;/p>
$$ C^d \equiv M \pmod N $$
&lt;p>앨리스는 $d$를 모르고 도청자도 $d$를 모르기 때문에, $C$에서 $M$을 꺼낼 수 있는 것은 $d$를 가지고 있는 밥뿐인 것입니다.&lt;/p>
&lt;hr>
&lt;h2 id="7-구체적인-예-작은-소수를-사용하여-손으로-직접-rsa를-계산해-보자">7. 구체적인 예: 작은 소수를 사용하여 손으로 직접 RSA를 계산해 보자
&lt;/h2>&lt;p>실제로 작은 숫자(소수)를 사용하여 앨리스에서 밥으로 암호 통신을 해봅시다.&lt;/p>
&lt;p>&lt;strong>【밥의 키 생성 단계】&lt;/strong>&lt;/p>
&lt;ol>
&lt;li>두 소수 $p=11$, $q=13$ 을 선택합니다.&lt;/li>
&lt;li>$N = 11 \times 13 = 143$ 을 계산합니다.&lt;/li>
&lt;li>$\phi(N) = (11 - 1) \times (13 - 1) = 10 \times 12 = 120$ 을 계산합니다.&lt;/li>
&lt;li>$\phi(N)=120$ 과 서로소인 공개키 $e$를 선택합니다. 여기서는 $e=7$ 로 하겠습니다.&lt;/li>
&lt;li>비밀키 $d$를 구합니다. $7 \times d \equiv 1 \pmod{120}$ 이 되는 $d$를 찾습니다.
방정식 $7d = 120k + 1$ 에서 $k=6$ 일 때 $721$이 되며, $721 \div 7 = 103$.
따라서 $d = 103$ 이 됩니다.&lt;/li>
&lt;/ol>
&lt;ul>
&lt;li>공개키: $(N=143, e=7)$&lt;/li>
&lt;li>비밀키: $d=103$&lt;/li>
&lt;/ul>
&lt;p>&lt;strong>【앨리스의 암호화 단계】&lt;/strong>
메시지 $M = 9$ 를 보내고 싶다고 합시다.
식: $C \equiv 9^7 \pmod{143}$
$9^7 = 4,782,969$. 이것을 143으로 나누면 몫이 $33447$이고 나머지가 $48$.
암호문 $C = 48$ 이 되었습니다.&lt;/p>
&lt;p>&lt;strong>【밥의 복호화 단계】&lt;/strong>
밥은 암호문 $C = 48$ 을 받고, 비밀키 $d = 103$ 을 사용하여 복호화합니다.
식: $M \equiv 48^{103} \pmod{143}$
계산기에서 &lt;code>(48 ** 103) % 143&lt;/code> 을 실행하면, 놀랍게도 결과는 &amp;lsquo;&lt;strong>9&lt;/strong>&amp;lsquo;가 됩니다! 원래 메시지를 무사히 받을 수 있었습니다.&lt;/p>
&lt;hr>
&lt;h2 id="8-비밀키-d를-구하는-방법-확장-유클리드-호제법">8. 비밀키 $d$를 구하는 방법: 확장 유클리드 호제법
&lt;/h2>&lt;p>수작업 계산 예제에서는 감으로 $k$를 찾아 $d=103$ 을 발견했지만, 수가 수백 자리가 되면 이 방법은 불가능합니다. 실제 프로그램에서는 **&amp;lsquo;확장 유클리드 호제법&amp;rsquo;**이라는 알고리즘을 사용합니다.&lt;/p>
&lt;p>$7d \equiv 1 \pmod{120}$ 을 푼다는 것은 $7d + 120y = 1$ 을 만족하는 정수 $d, y$ 를 찾는 것과 같습니다. 유클리드 호제법을 역산해 나감으로써 이를 기계적으로 구할 수 있습니다.&lt;/p>
&lt;ol>
&lt;li>$120 \div 7 = 17$ 나머지 $1$&lt;/li>
&lt;li>이것을 변형하면 $1 = 120 - 17 \times 7$&lt;/li>
&lt;li>즉, $-17 \times 7 \equiv 1 \pmod{120}$&lt;/li>
&lt;/ol>
&lt;p>$-17$ 은 법 $120$의 세계에서는 $120 - 17 = 103$ 과 같은 의미가 됩니다. 따라서 $d = 103$ 을 순식간에 구할 수 있습니다. 이 방법은 아무리 큰 수라도 매우 빠르게 계산할 수 있습니다.&lt;/p>
&lt;hr>
&lt;h2 id="9-rsa-암호의-또-다른-얼굴-디지털-서명">9. RSA 암호의 또 다른 얼굴: 디지털 서명
&lt;/h2>&lt;p>RSA 암호의 훌륭한 점은 공개키와 비밀키의 역할을 반대로 하여 **&amp;lsquo;디지털 서명&amp;rsquo;**으로도 사용할 수 있다는 것입니다.&lt;/p>
&lt;p>암호화할 때는 &amp;lsquo;공개키로 암호화 $\Rightarrow$ 비밀키로 복호화&amp;rsquo;였지만,
디지털 서명에서는 &amp;lsquo;비밀키로 암호화 $\Rightarrow$ 공개키로 복호화&amp;rsquo;라는 절차를 밟습니다.&lt;/p>
&lt;div class="mermaid">flowchart TD
A1["1. 앨리스가 비밀키로 서명 생성"] --> A2["S ≡ M^d (mod N)"]
A2 --> A3["메시지 M과 서명 S를 전송"]
A3 --> B1["2. 밥이 공개키로 서명 검증"]
B1 --> B2["M' ≡ S^e (mod N) 을 계산"]
B2 --> B3["M'과 M이 일치하는지 확인"]&lt;/div>
&lt;p>앨리스가 자신의 비밀키 $d$를 사용하여 메시지를 변환하고(이것이 서명 $S$), 밥에게 보냅니다. 밥은 앨리스의 공개키 $e$를 사용하여 검증 계산을 합니다. 만약 계산 결과가 원래 메시지와 일치한다면, &amp;lsquo;앨리스의 비밀키로만 만들 수 있는 데이터이다&amp;rsquo;라는 것과 &amp;lsquo;메시지가 중간에 위조되지 않았다&amp;rsquo;는 것이 동시에 증명되는 것입니다.&lt;/p>
&lt;hr>
&lt;h2 id="10-프로그램으로-체감하는-rsa-암호">10. 프로그램으로 체감하는 RSA 암호
&lt;/h2>&lt;p>수작업으로는 힘든 거듭제곱 계산도 파이썬을 사용하면 매우 쉽게 구현할 수 있습니다. 다음은 RSA 암호의 핵심 로직을 체험할 수 있는 파이썬 코드입니다.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;span class="lnt">21
&lt;/span>&lt;span class="lnt">22
&lt;/span>&lt;span class="lnt">23
&lt;/span>&lt;span class="lnt">24
&lt;/span>&lt;span class="lnt">25
&lt;/span>&lt;span class="lnt">26
&lt;/span>&lt;span class="lnt">27
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="k">def&lt;/span> &lt;span class="nf">gcd&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">a&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">b&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;&amp;#34;&amp;#34;최대공약수를 구한다&amp;#34;&amp;#34;&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">while&lt;/span> &lt;span class="n">b&lt;/span> &lt;span class="o">!=&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">a&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">b&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">b&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">a&lt;/span> &lt;span class="o">%&lt;/span> &lt;span class="n">b&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="n">a&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">def&lt;/span> &lt;span class="nf">mod_inverse&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">e&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">phi&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;&amp;#34;&amp;#34;비밀키 d를 구한다 (Python 3.8 이후의 내장 기능 활용)&amp;#34;&amp;#34;&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="nb">pow&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">e&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="o">-&lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">phi&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 1. 키 생성&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">p&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">q&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">11&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">13&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">N&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">p&lt;/span> &lt;span class="o">*&lt;/span> &lt;span class="n">q&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">phi&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">p&lt;/span> &lt;span class="o">-&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">*&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">q&lt;/span> &lt;span class="o">-&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">e&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">7&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">d&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">mod_inverse&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">e&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">phi&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nb">print&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="sa">f&lt;/span>&lt;span class="s2">&amp;#34;공개키: (N=&lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">N&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2">, e=&lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">e&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2">), 비밀키: d=&lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">d&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 2. 암호화&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">message&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">9&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">ciphertext&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nb">pow&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">message&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">e&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">N&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nb">print&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="sa">f&lt;/span>&lt;span class="s2">&amp;#34;암호문: &lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">ciphertext&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 3. 복호화&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">decrypted_message&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nb">pow&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">ciphertext&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">d&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">N&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nb">print&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="sa">f&lt;/span>&lt;span class="s2">&amp;#34;복호화된 메시지: &lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">decrypted_message&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>파이썬의 &lt;code>pow(base, exp, mod)&lt;/code> 함수는 내부적으로 &amp;lsquo;반복 제곱법(거듭제곱법)&amp;lsquo;이라는 고속 알고리즘을 사용하기 때문에 수백 자리의 숫자라도 순식간에 계산이 끝납니다.&lt;/p>
&lt;hr>
&lt;h2 id="11-요약-및-미래의-암호-기술">11. 요약 및 미래의 암호 기술
&lt;/h2>&lt;p>고등학교 수학 지식을 바탕으로 RSA 암호의 원리를 밝혀 보았습니다.&lt;/p>
&lt;ol>
&lt;li>&lt;strong>소인수분해의 어려움:&lt;/strong> $p \times q = N$ 은 쉽지만, $N$에서 $p, q$를 찾는 것은 매우 어렵다.&lt;/li>
&lt;li>&lt;strong>합동식과 오일러의 정리:&lt;/strong> $a^{\phi(N)} \equiv 1 \pmod N$ 이라는 법칙에 의해, &amp;lsquo;어떤 수로 거듭제곱하면 원래대로 돌아가는&amp;rsquo; 마법의 트랩도어가 완성된다.&lt;/li>
&lt;li>&lt;strong>공개키와 비밀키:&lt;/strong> 누구나 암호화할 수 있지만, 복호화할 수 있는 것은 정당한 수신자뿐이다.&lt;/li>
&lt;/ol>
&lt;p>현재 사용되는 RSA 암호의 $N$은 600자리 이상이며, 전 세계의 슈퍼컴퓨터를 총동원해도 소인수분해에는 우주의 나이 이상의 시간이 걸립니다. 하지만 최근 연구가 진행되고 있는 &amp;lsquo;양자 컴퓨터&amp;rsquo;가 장래에 실용화되면 &amp;lsquo;쇼어의 알고리즘(Shor&amp;rsquo;s algorithm)&amp;lsquo;에 의해 이 소인수분해가 순식간에 풀릴 가능성이 있습니다. 따라서 현재는 양자 컴퓨터로도 해독할 수 없는 &amp;lsquo;양자 내성 암호(Post-Quantum Cryptography)&amp;rsquo; 개발이 전 세계적으로 빠른 속도로 진행되고 있습니다.&lt;/p>
&lt;p>&amp;lsquo;쓸모없다&amp;rsquo;고 여겨지기 쉬운 고도화된 수학이 사실은 우리의 일상생활을 근본적으로 지켜주고 있습니다. RSA 암호는 그런 수학의 깊이와 아름다움을 가르쳐 주는 최고의 교재입니다. 이 글을 통해 암호와 수학의 재미를 조금이라도 느끼셨기를 바랍니다.&lt;/p></description></item></channel></rss>