\documentclass[12pt,fleqn,a4paper]{book}
% 
% Packages used:
%
\usepackage[koi8-r]{inputenc}
\usepackage[russian,english]{babel}
\usepackage{amsmath}
\usepackage{amssymb}
\usepackage{amsfonts}
\usepackage{amsthm}
\usepackage{fullpage}
\advance\textheight by 5mm
%
% Common customization:
%
\input defs
%
\renewcommand\mod{\mathrm{mod}\ }
\newcommand\pri{\mathtt{pri}}
\newcommand\pub{\mathtt{pub}}
\newcommand\code{\mathtt{code}}
%
% The document
%
\begin{document}
\selectlanguage{russian}
\lecture{1}{Введение в криптографию}{В. Моргенштерн}
%
% The lecture
%
\section{Постановка задачи}

В криптографии существует множество задач, которые имеют широкую
область применения. Это и закрытая передача данных по открытым
каналам, и алгоритмы электронной подписи, и интерактивные
доказательства с нулевым разглашением, и многие другие. Изучение
криптографии мы начнем с так называемых ``протоколов с открытым
ключом''. Задача состоит в следующем. Имеется читатель A и писатель
B. Проблема заключается в том, чтобы передать данные от B к A в
зашифрованном виде, чтобы никто из тех, кто перехватит данные на
пути из B к A, не смог расшифровать их. Кроме того, необходимо, чтобы
перехватчик также не мог модифицировать зашифрованное сообщение
и, таким образом, подсунуть A ложную информацию. В начале
работы протокола у A и B нет никакой общей конфиденциальной
информации. Канал между A и B все время открыт, то есть читать из
него может кто угодно.

\section{Криптосистема с открытым ключом\protect\footnote{Public Key Encryption Scheme}}
Неформально, идея системы, которая бы позволила нам решить
поставленную задачу, состоит в следующем. Первоначально читатель A
создает пару ключей. Один ключ ($\pub$) публикается. Он общедоступен
и называется открытым ключом. Второй ключ ($\pri$) читатель оставляет
у себя и хранит в секрете. Этот ключ называется закрытым (личным,
приватным). Алгоритм кодирования устроен так, что любой может
закодировать сообщение, зная открытый ключ. Задача раскодирования
сообщения легко решается, если известен закрытый ключ ($\pri$). Если
этот ключ врагу не известен, то раскодировать такое такое
сообщение будет крайне трудно. Тем труднее, чем длиннее были
выбраны ключи $\pri$ и $\pub$. Существование подобной схемы целиком и
полностью опирается на гипотезы теории сложности, о которых пойдет
речь в дальнейшем.

\begin{definition}
Для формального построения криптосистемы нам понадобятся следующие алгоритмы.
\begin{description}
\item[Генератор ключей] ---
вероятностный алгоритм с полиномиальным математическим ожиданием
времени работы
\[
G:1^k\mapsto (\pub,\pri),
\]
где $|\pub|=|\pri|=k$.
\item[Шифровальщик] ---
вероятностный алгоритм с полиномиальным математическим ожиданием
времени работы
\[
E:(m, \pub)\mapsto \code.
\]
\item[Дешифровщик] ---
вероятностный алгоритм с полиномиальным математическим ожиданием
времени работы
\[
D:(\code, \pri)\mapsto m.
\]
\end{description}
\end{definition}
От шифровальщика и дешифровщика, мы, естественно, должны
дополнительно потребовать малую вероятность ошибки:
для любого сообщения $m$
вероятность события $D(E(m,\pub), \pri)\neq m$ --- константа
(единая для всех $m$).

\section{Односторонние функции}
Определенные выше функции еще не дают нам никаких оснований
полагать, что криптосистема будет надежной. Для того, чтобы создать
надежную криптосистему,
нам понадобится понятие семейства односторонних перестановок с
секретом\footnote{family of trapdoor permutations}.
Чтобы дать определение такого семейства, мы сначала определим односторонние
функции\footnote{one-way function} и односторонние
перестановки\footnote{one-way permutation}.
\begin{definition}
\emph{Односторонней функцией} называется функция
\[
f:X\rightarrow Y,
\]
вычислимая за полиномиальное время,
обратная к которой не может быть вычислена за полиномиальное время.
(Обратная --- любая функция
$
g:Y\rightarrow X,
$
такая, что $\forall y\in\mathbf{Im} f\ g(y)\in f^{-1}(y)$.)
\begin{remark}
Вторая компонента ($\pub$) генератора ключей должна быть односторонней
функцией от используемых случайных битов (в противном случае можно было
бы вычислить по $\pub$ случайные биты, а по ним --- $\pri$).
Это одно необходимых (но вовсе не достаточных!) условий надежности
криптосистемы. О достаточных мы будем говорить много позже.\hfill$\qed$
\end{remark}

В дальнейшем одностороннюю функцию будет удобно понимать как
последовательность булевых схем $f_i:X\cap \{0,1\}^i\to Y$,
описания
которых можно породить полиномиальным по времени алгоритмом: $1^{i}\mapsto f_i$.
\end{definition}
\begin{theorem}
$\P\neq\NP$ тогда и только тогда, когда существуют односторонние функции.
\end{theorem}
\begin{proof}
\fbox{$\Rightarrow$}
Действительно, пусть $F$ --- формула в КНФ, а $A$ --- набор значений
переменных. Определим одностороннюю функцию:
\[
f(F,A)=
(F,F[A])
.
\]
Если бы могли обратить эту функцию, мы могли бы найти выполняющий
набор для любой выполнимой формулы (вычислив $f^{-1}(F,\mathtt{True})$).
Но мы знаем, что эта задача $\widetilde{\NP}$-полна.

\fbox{$\Leftarrow$}
Задача обращения функции, вычислимой за полиномиальное время,
очевидно, принадлежит $\widetilde{\NP}$.
\end{proof}

\begin{definition}
\emph{Односторонней перестановкой}
называется односторонняя функция,
которая является инъективной.
\end{definition}
Легко доказать аналог предыдущей теоремы.
\begin{theorem}\label{th:owp}
$\P\neq\UP$ тогда и только тогда, когда существуют односторонние перестановки.
\end{theorem}
\begin{remark}
Здесь $\UP$ --- класс языков, принимаемых НМТ с ровно одним принимающим путем
для слов из языка и нулем принимающих путей для слов не из языка.
\end{remark}
\begin{proof}
Для НМТ $M$, задающей язык из $\UP\setminus\P$, определим
\[ 
f(M,x,\mbox{путь вычислений $A$ на входе $x$})=
\begin{cases}
(M,x,A),&\mbox{$A$ --- отвергающий},\\
(M,x),&\mbox{$A$ --- принимающий}.
\end{cases}
\]
Обратное очевидно.
\end{proof}

Усовершенствуем понятие односторонней перестановки.

\begin{definition}
Семейством перестановок <<с секретом>>\footnote{trapdoor permutations},
сохраняющих длину,
называется семейство булевых схем $\{f_i\}_{i\in I}$, 
где $\forall i\in I\ f_i:D_i\to D_i$ 
является перестановкой;
любая возрастающая (по $|i|$)
последовательность этих перестановок является односторонней перестановкой, 
$D_i\subseteq\{0,1\}^{|i|}$, и 
существуют вероятностные алгоритмы $G:1^k\mapsto (f_i,t_i)$ (где $|i|=k$) и 
$g^*:(c,t_i)\mapsto f_i^{-1}(c)$.
\end{definition}
\begin{remark}
Важно, что семейство содержит много функций $f_i$ для одной длины входа $|i|$.
\end{remark}
\begin{remark}
Аналогично можно определить семейство односторонних функций <<с секретом>>,
а также семейство односторонних перестановок <<с секретом>>,
необязательно сохраняющих длину.\hfill$\qed$
\end{remark}
\begin{remark}
Из любой надежной криптосистемы можно получить
семейство перестановок с секретом
(возможно, не сохраняющих длину)
следующим способом.
Положим 
$I=\mathbf{Im}(G)$, 
$D_i=\{0,1\}^{|i|}$,
для $m\in D_i$ 
\[
f_i(m)=E(m,i): D_i\rightarrow\{0,1\}^{\poly(|i|)}.
\]
(Здесь использовано, что ``надежная'' криптосистема должна ``надежно''
шифровать хотя бы сообщения той же длины, что и используемый ключ.)
\phantom{WWW}\hfill$\qed$
\end{remark}

Теперь мы можем попытаться построить криптосистему,
предполагая, что такое семейство существует. Итак,
генератор 
\[
G:1^k\mapsto (f_i,t_i)
\]
выдает нам пару (функция --- публичный ключ, ее секрет --- приватный ключ);
шифровальщик
\[
E:(m,f_i)\mapsto f_i(m)
\]
применяет одностороннюю перестановку, а
дешифровщик
\[
D:(\mathtt{code},t)\mapsto g^*(\mathtt{code},t)
\]
обращает ее, зная секрет.

Однако, в этой системе есть несколько неприятностей:
\begin{itemize}
\item алгоритм $E$ --- детерминированный; поэтому, однажды узнав
      $E(0,f_i)$, мы всегда сможем его отличить от $E(1,f_i)$
      (если, конечно, не будем менять ключ каждый раз --- но это
      крайне неэффективно, особенно если <<писателей>> --- несколько);

\begin{remark}
Если же $E$ --- вероятностный,
или мы действительно каждый раз используем новый ключ
(что в некотором смысле --- то же самое),
то каждой криптосистеме соответствует
естественным образом \emph{promise problem}:
в предположении (``promise''), что вход $x$ --- код нуля или единицы,
ответить, кодом чего же именно является $x$.

Вычислительная трудность этой проблемы является, очевидно,
необходимым условием надежности побитного кодирования.
Между тем, каждое из множеств ``коды нуля'' $Z_0$ и ``коды единицы'' $Z_1$
принадлежит $\NP$ (строкой подсказки являются случайные биты
алгоритма кодирования и случайные биты ключа --- подразумевается,
что открытый ключ также входит в ``код''). Если все строки являются
кодами чего-либо (либо коды легко отличить от не-кодов),
то эти множества (и, неформально говоря, задача раскодирования)
оказываются в $\NP\cap\coNP$.
В противном случае задача раскодирования --- это так называемая
\emph{дизъюнктная $\NP$-пара}, а алгоритм взлома --- \emph{разделитель}
для этой $\NP$-пары (он должен правильно отвечать, какому из множеств
принадлежит строка, но только если строка принадлежит $Z_0\cup Z_1$;
в противном случае он может выдавать что угодно).
%Тем не менее, если $\P\neq\UP$, такие $Z_0,Z_1\in\NP$, не разделимые
%$\P$-множеством, существуют.
\hfill$\qed$
\end{remark}

\item мы требовали от односторонних функций трудности в наихудшем случае ---
      но что, если их будет легко обратить как раз на интересующем
      нас сообщении (или можно будет вычислить какую-то полезную
      функцию от сообщения --- первую его половину, например).
\end{itemize}
\emph{В дальнейшем мы будем с ними бороться.}

\section{Пример: RSA}
В заключение,
мы опишем самый простой и популярный алгоритм шифрования с открытым ключом, 
который называется RSA, по первым буквам фамилий его авторов
(Rivest, Shamir, Adleman). В таком виде алгоритм, конечно, 
не является надежным, но это служит базой для многочисленных модификаций,
которые на самом деле применяются на практике.

Итак выберем два очень больших простых числа $p,q$.
Вычислим число $n=pq$.
Выберем число $e$ так, чтобы $\mbox{НОД}(e,p-1)=\mbox{НОД}(e,q-1)=1$,
то есть $\mbox{НОД}(e,\phi(n))=1$.
Далее, находим $d\in [1.. n]$, такое, что $de\equiv1\ (\mod\phi(n))$.
Оно, очевидно, существует и единственно в силу того, что $\mbox{НОД}(e,\phi(n))=1$.
\begin{exercise}
А нужное $e$ как найти?
\end{exercise}

Итак, публичный ключ $\pub=(e,n)$;
приватный ключ $\pri=(d,n)$.
Функция кодирования 
$$
f_{n,e}(x)=x^e\ \mod n.
$$  
Для декодирования мы просто возводим код в степень $d$
$$
g^*_d(y)=y^d\ \mod n.
$$
При этом 
по теореме Эйлера 
$$
g^*_d(x^e)\equiv
x^{ed}\equiv (x^{\phi(n)})^kx\equiv x\ (\mod n).
$$
Мы видим, что зная $d$, 
мы можем элементарно раскодировать сообщение за полиномиальное время, 
однако если $d$ неизвестно, непонятно, как это можно было бы сделать.

\begin{remark}
К сожалению, надежность модификаций этой системы (как и других)
основывается не на общих гипотезах типа $\P=\NP$,
а на том, что \emph{конкретные} функции являются односторонними
(в данном случае --- $f_{n,e}$).
\end{remark}
\vfill
\begin{flushright}{\tt\small Последнее исправление: 24.04.2005, 12:30}\end{flushright}


\end{document}

