Здравствуйте, уважаемые хабровчане! По мере моего посильного занятия криптографией для своих скромных нужд, в попытках поддержать достойный уровень безопасности данных (я ориентируюсь на уровни, указанные в разделе ecrypt тут) меня начало беспокоить падение производительности при использовании криптоалгоритма RSA.
К сожалению, этот алгоритм оказался единственным в openssl, который допускает шифрование/дешифровку маленьких блоков данных (предполагается по смыслу статьи — ключей для алгоритмов симметричного шифрования) с помощью асимметричных криптоалгоритмов.
Прогулявшись по просторам интернета, удалось выяснить, что:
1. El-Gamal может успешно шифровать/расшифровывать, но эти операции не реализованы в openssl (есть реализация в libgcrypt). В плане быстродействия El-Gamal раза в 3 быстрее RSA
при той-же длине ключа и той-же криптостойкости на 1 бит ключа.
2. Elliptic Curve cryptosystem (ECC) приятно удивили скоростью и криптостойкостью на 1 бит ключа, но операции шифрования/дешифрования на основе ECC не реализованы в openssl.
Реализация ECC шифрования в libgcrypt есть, но очень специфична. Если коротко, то шифруемое сообщение m отображается на точку эллиптической кривой mG, из которой исходное сообщение m не может быть получено иначе, как взломом ECC или перебором всех возможных значений m.
3. В литературе [1] описана Menezes-Vanstone ECC, но есть уведомления о ее «уязвимости» [2]
Разберем этот вопрос подробнее.
Для простоты будем говорить только о эллиптических кривых, вид которых описывается уравнением Вейерштрасса над полями целых чисел, определенных как Zp, где Zp — множество целых чисел, меньших некоторого простого числа р и больших нуля.
Тогда E(p,a,b) — где a,b принадлежат Zp — эллиптическая кривая над полем Zp, определяемая простым числом p и числами a,b. Далее нужно определить абстрактный нулевой элемент O (если коэффициент уравнения Вейершрасса b не равен 0, то за условную точку O можно принять координаты x=0,y=0 даже если эта точка и не является решением уравнения) и операцию сложения элементов кривой(точек), которая даст новую точку, принадлежащую этой-же кривой.
Естественно, должно получиться, что P+Q=Q+P, (P+Q)+R = P+(Q+R), P+O=O+P=P и если есть P(x,y), то есть -P=(x,-y) и P+(-P)=P-P=O.
Это — все математические операции, которые определены для группы точек эллиптической кривой.
В литературе [1] можно найти математические подробности, как определяются эти операции.
Складывать можно разные точки (P=G+Q) или точку с собой (P=Q+Q). То, что мы будем говорить о «умножении» — лишь способ сократить запись, и не писать P=Q+Q+Q+Q+...+Q m раз, а просто написать, что P=mQ. На самом деле нет операции «умножение» и, соответственно «деление», также как нет «возведения в степень» и «взятия логарифма».
Эта терминология часто используется, но для эллиптических кривых обозначает она не то, что обычно под этим понимается. Суммирование точки на эллиптической кривой с собой m раз можно назвать «умножением на m» или даже «возведением в степень m». Суть от этого не меняется, а поскольку обратной операции, «деления» или «взятия логарифма» не существует, получить m из точки m*G, даже зная G невозможно, говорят о «проблеме дискретного логарифма на эллиптических кривых». Такова устоявшаяся терминология.
На этой кривой выбирается (произвольная) точка G(Gx,Gy) которая является генератором группы точек, то есть, задавая разные m, получаем результат умножения mG, который образует циклическую группу точек (поскольку мы в конечном поле Zp). Размер этой циклической группы называется порядком (order) точки генератора G.
Таким образом, эллиптическая группа полностью описывается параметрами кривой E(p,a,b), точкой генератора G(Gx,Gy), и порядком группы ord, при этом ord*G=O. Это все называется параметрами эллиптической кривой, которые обычно являются общеизвестными, и идентифицируется по именам, например secp192k1 или prime256v1.
Приватным ключом пользователя является (секретное, случайное) число 1 < d < ord-1
Публичным ключом пользователя является точка Q, которая является произведением приватного ключа d и генератора группы G, Q=dG.
Сторона отправителя:
1. Шифруемое сообщение m разбивается на две части x1 и x2, каждая из которых должна быть элементом поля Zp, для этого достаточно проверить их длину и сравнить с длиной параметра кривой p.
2. Отправитель выбирает (секретное, случайное) число 1 < ks < ord-1.
3. Отправитель умножает точку генератора G на число ks, y0=ks*G
4. Отправитель вычисляет точку Z(Zx,Zy), умножая публичный ключ получателя Q на число ks, Z=ks*Q
5. Отправитель вычисляет y1=x1*Zx(mod p), y2=x2*Zy(mod p)
Вычислительные затраты отправителя: генерация случайного числа нужной длины, 2 операции умножения точки на число, 2 операции умножения по модулю p.
Шифротекстом является точка y0, число y1, число y2. Точка содержит 2 числа — координаты x,y. Общий объем шифротекста приблизительно равен 4*p, для ключа ECC длиной 192 бита (24 байта) приблизительно 24*4=96 байт.
Сторона получателя:
1. Получатель проверяет принадлежность точки y0 кривой, заданной параметрами E(p,a,b),G,ord.
2. Получатель вычисляет точку Z, умножая шифротекст y0 на свой приватный ключ d, Z=d*y0=d*ks*G=ks*d*G=ks*Q.
3. Получатель вычисляет мультипликативную инверсию компонентов Z(Zx,Zy), e1=inv(Zx)(mod p), e2=inv(Zy)(mod p).
4. Получатель восстанавливает x1, x2: x1=y1*e1(mod p), x2=y2*e2(mod p).
Вычислительные затраты получателя: проверка принадлежности точки к кривой, 1 операция умножения точки на число, 2 операции вычисления мультипликативной инверсии по модулю p,
2 операции умножения по модулю p.
В 1997г Klaus Kiefer [2] показал, что MVC не является системой, использующей вероятностное шифрование, вопреки своему дизайну. Зная шифротекст, зная параметры кривой, можно провести «known plaintext attack» (атака с угадыванием открытого текста).
Как это выглядит:
Известны параметры кривой E(p,a,b),G,ord. Известен шифротекст y0,y1,y2. Мы предполагаем, что открытым текстом является x1,x2.
Если точка F(f1,f2) f1=y1*(inv(x1))(mod p), f2=y2*(inv(x2))(mod p) принадлежит кривой E(p,a,b), то с вероятностью ошибки 1/p x1,x2 действительно есть искомый открытый текст.
Что это означает на практике?
То, что возможно устроить перебор всех значений x1,x2 с вычислительными затратами 2 операции вычисления мультипликативной инверсии по модулю p, 2 операции умножения по модулю p на каждый вариант x1,x2 и с вероятностью ошибки 1/p найти открытый текст, соответствующий зашифрованному. Отрицательный результат проверки всегда верен, положительный может содержать ошибочное распознавание с вероятностью 1/p. Операция перебора x1,x2 хорошо распареллеливается, много процессов могут независимо перебирать свои неперекрывающиеся диапазоны значений x1,x2.
Для справки: я бы хотел посмотреть на перебор (просто перебор, без вычислений) всех возможных значений ключа 192 или 256 бит. Да, даже и 128-168 бит.
Понятно, что перебором можно вскрыть только небольшие кусочки зашифрованных данных, до 48-64 бит. И перебор этот можно устроить без MVC, для задачи найти ключ перебором всех возможных значений использование MVC не нужно, это лишняя сущность.
Если шифровать достаточно большие (128 бит и более) тексты, исключающие возможность за разумное время и приемлемые затраты найти их перебором, данная «уязвимость» не играет никакой роли.
На сегодняшний день минимальная рекомендуемая длина ключа ECC — 192 бита (24 байта). Длина шифруемых данных MVC в этом случае не должна превышать 2*24=48 байт, а самый стойкий ключ AES или GOST, имеет длину 256 бит (32 байта).
Зато мы получаем криптостойкий и достаточно быстрый (по моим прикидкам, ECC-224 в 5 раз быстрее RSA-2048, в 3 раза быстрее ElGamal-2048) алгоритм асимметричного шифрования.
Я считаю, что Elliptic curve Menezes-Vanstone cryptosystem совершенно незаслуженно забыта.
Пытаясь восполнить этот пробел, выкладываю исходники на языке C с использованием openssl API.
Литература:
1. COMPUTER SECURITY AND CRYPTOGRAPHY, ALAN G. KONHEIM, Published by John Wiley & Sons, Inc., Hoboken, New Jersey, 2007. ISBN-13: 978-0-471-94783-7 ISBN-10: 0-471-94783-0
2. «A Weakness of Menezes-Vanstone Cryptosystem», Klaus Kiefer, member of research group of prof. J. Buchmann, 1997
Спасибо за внимание!
К сожалению, этот алгоритм оказался единственным в openssl, который допускает шифрование/дешифровку маленьких блоков данных (предполагается по смыслу статьи — ключей для алгоритмов симметричного шифрования) с помощью асимметричных криптоалгоритмов.
Прогулявшись по просторам интернета, удалось выяснить, что:
1. El-Gamal может успешно шифровать/расшифровывать, но эти операции не реализованы в openssl (есть реализация в libgcrypt). В плане быстродействия El-Gamal раза в 3 быстрее RSA
при той-же длине ключа и той-же криптостойкости на 1 бит ключа.
2. Elliptic Curve cryptosystem (ECC) приятно удивили скоростью и криптостойкостью на 1 бит ключа, но операции шифрования/дешифрования на основе ECC не реализованы в openssl.
Реализация ECC шифрования в libgcrypt есть, но очень специфична. Если коротко, то шифруемое сообщение m отображается на точку эллиптической кривой mG, из которой исходное сообщение m не может быть получено иначе, как взломом ECC или перебором всех возможных значений m.
3. В литературе [1] описана Menezes-Vanstone ECC, но есть уведомления о ее «уязвимости» [2]
Разберем этот вопрос подробнее.
Немного математики:
Для простоты будем говорить только о эллиптических кривых, вид которых описывается уравнением Вейерштрасса над полями целых чисел, определенных как Zp, где Zp — множество целых чисел, меньших некоторого простого числа р и больших нуля.
Тогда E(p,a,b) — где a,b принадлежат Zp — эллиптическая кривая над полем Zp, определяемая простым числом p и числами a,b. Далее нужно определить абстрактный нулевой элемент O (если коэффициент уравнения Вейершрасса b не равен 0, то за условную точку O можно принять координаты x=0,y=0 даже если эта точка и не является решением уравнения) и операцию сложения элементов кривой(точек), которая даст новую точку, принадлежащую этой-же кривой.
Естественно, должно получиться, что P+Q=Q+P, (P+Q)+R = P+(Q+R), P+O=O+P=P и если есть P(x,y), то есть -P=(x,-y) и P+(-P)=P-P=O.
Это — все математические операции, которые определены для группы точек эллиптической кривой.
В литературе [1] можно найти математические подробности, как определяются эти операции.
Складывать можно разные точки (P=G+Q) или точку с собой (P=Q+Q). То, что мы будем говорить о «умножении» — лишь способ сократить запись, и не писать P=Q+Q+Q+Q+...+Q m раз, а просто написать, что P=mQ. На самом деле нет операции «умножение» и, соответственно «деление», также как нет «возведения в степень» и «взятия логарифма».
Эта терминология часто используется, но для эллиптических кривых обозначает она не то, что обычно под этим понимается. Суммирование точки на эллиптической кривой с собой m раз можно назвать «умножением на m» или даже «возведением в степень m». Суть от этого не меняется, а поскольку обратной операции, «деления» или «взятия логарифма» не существует, получить m из точки m*G, даже зная G невозможно, говорят о «проблеме дискретного логарифма на эллиптических кривых». Такова устоявшаяся терминология.
На этой кривой выбирается (произвольная) точка G(Gx,Gy) которая является генератором группы точек, то есть, задавая разные m, получаем результат умножения mG, который образует циклическую группу точек (поскольку мы в конечном поле Zp). Размер этой циклической группы называется порядком (order) точки генератора G.
Таким образом, эллиптическая группа полностью описывается параметрами кривой E(p,a,b), точкой генератора G(Gx,Gy), и порядком группы ord, при этом ord*G=O. Это все называется параметрами эллиптической кривой, которые обычно являются общеизвестными, и идентифицируется по именам, например secp192k1 или prime256v1.
Приватным ключом пользователя является (секретное, случайное) число 1 < d < ord-1
Публичным ключом пользователя является точка Q, которая является произведением приватного ключа d и генератора группы G, Q=dG.
Что предлагает Elliptic curve Menezes-Vanstone cryptosystem [1] (MVC)?
Сторона отправителя:
1. Шифруемое сообщение m разбивается на две части x1 и x2, каждая из которых должна быть элементом поля Zp, для этого достаточно проверить их длину и сравнить с длиной параметра кривой p.
2. Отправитель выбирает (секретное, случайное) число 1 < ks < ord-1.
3. Отправитель умножает точку генератора G на число ks, y0=ks*G
4. Отправитель вычисляет точку Z(Zx,Zy), умножая публичный ключ получателя Q на число ks, Z=ks*Q
5. Отправитель вычисляет y1=x1*Zx(mod p), y2=x2*Zy(mod p)
Вычислительные затраты отправителя: генерация случайного числа нужной длины, 2 операции умножения точки на число, 2 операции умножения по модулю p.
Шифротекстом является точка y0, число y1, число y2. Точка содержит 2 числа — координаты x,y. Общий объем шифротекста приблизительно равен 4*p, для ключа ECC длиной 192 бита (24 байта) приблизительно 24*4=96 байт.
Сторона получателя:
1. Получатель проверяет принадлежность точки y0 кривой, заданной параметрами E(p,a,b),G,ord.
2. Получатель вычисляет точку Z, умножая шифротекст y0 на свой приватный ключ d, Z=d*y0=d*ks*G=ks*d*G=ks*Q.
3. Получатель вычисляет мультипликативную инверсию компонентов Z(Zx,Zy), e1=inv(Zx)(mod p), e2=inv(Zy)(mod p).
4. Получатель восстанавливает x1, x2: x1=y1*e1(mod p), x2=y2*e2(mod p).
Вычислительные затраты получателя: проверка принадлежности точки к кривой, 1 операция умножения точки на число, 2 операции вычисления мультипликативной инверсии по модулю p,
2 операции умножения по модулю p.
Уязвимость или слабость MVC
В 1997г Klaus Kiefer [2] показал, что MVC не является системой, использующей вероятностное шифрование, вопреки своему дизайну. Зная шифротекст, зная параметры кривой, можно провести «known plaintext attack» (атака с угадыванием открытого текста).
Как это выглядит:
Известны параметры кривой E(p,a,b),G,ord. Известен шифротекст y0,y1,y2. Мы предполагаем, что открытым текстом является x1,x2.
Если точка F(f1,f2) f1=y1*(inv(x1))(mod p), f2=y2*(inv(x2))(mod p) принадлежит кривой E(p,a,b), то с вероятностью ошибки 1/p x1,x2 действительно есть искомый открытый текст.
Что это означает на практике?
То, что возможно устроить перебор всех значений x1,x2 с вычислительными затратами 2 операции вычисления мультипликативной инверсии по модулю p, 2 операции умножения по модулю p на каждый вариант x1,x2 и с вероятностью ошибки 1/p найти открытый текст, соответствующий зашифрованному. Отрицательный результат проверки всегда верен, положительный может содержать ошибочное распознавание с вероятностью 1/p. Операция перебора x1,x2 хорошо распареллеливается, много процессов могут независимо перебирать свои неперекрывающиеся диапазоны значений x1,x2.
Для справки: я бы хотел посмотреть на перебор (просто перебор, без вычислений) всех возможных значений ключа 192 или 256 бит. Да, даже и 128-168 бит.
Понятно, что перебором можно вскрыть только небольшие кусочки зашифрованных данных, до 48-64 бит. И перебор этот можно устроить без MVC, для задачи найти ключ перебором всех возможных значений использование MVC не нужно, это лишняя сущность.
Что мы имеем в итоге?
Если шифровать достаточно большие (128 бит и более) тексты, исключающие возможность за разумное время и приемлемые затраты найти их перебором, данная «уязвимость» не играет никакой роли.
На сегодняшний день минимальная рекомендуемая длина ключа ECC — 192 бита (24 байта). Длина шифруемых данных MVC в этом случае не должна превышать 2*24=48 байт, а самый стойкий ключ AES или GOST, имеет длину 256 бит (32 байта).
Зато мы получаем криптостойкий и достаточно быстрый (по моим прикидкам, ECC-224 в 5 раз быстрее RSA-2048, в 3 раза быстрее ElGamal-2048) алгоритм асимметричного шифрования.
Я считаю, что Elliptic curve Menezes-Vanstone cryptosystem совершенно незаслуженно забыта.
Пытаясь восполнить этот пробел, выкладываю исходники на языке C с использованием openssl API.
Литература:
1. COMPUTER SECURITY AND CRYPTOGRAPHY, ALAN G. KONHEIM, Published by John Wiley & Sons, Inc., Hoboken, New Jersey, 2007. ISBN-13: 978-0-471-94783-7 ISBN-10: 0-471-94783-0
2. «A Weakness of Menezes-Vanstone Cryptosystem», Klaus Kiefer, member of research group of prof. J. Buchmann, 1997
//
// DESCRIPTION 'EC Menezes-Vanstone cryptosystem functions openssl/Linux'
// COMPILER 'gcc (GCC) 4.8.2'
// FILE 'ecmv.h'
// AUTHOR "Nick Korepanov"
// Linux-3.10.104, glibc-2.17, OpenSSL 1.0.1u
// ECC-192/224/256
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
//
// Author of this program can be contacted by electronic mail
// korepanovnd@gmail.com
// Copyright (c) 2017 Nick Korepanov. All rights reserved.
// This product includes software developed by the OpenSSL Project
// for use in the OpenSSL Toolkit. (http://www.openssl.org/)
// COMPUTER SECURITY AND CRYPTOGRAPHY, ALAN G. KONHEIM
// Published by John Wiley & Sons, Inc., Hoboken, New Jersey, 2007
// Library of Congress Cataloging-in-Publication Data:
// Konheim, Alan G., 1934–
// Computer security & cryptography / by Alan G. Konheim.
// p. cm.
// Includes bibliographical references and index.
// ISBN-13: 978-0-471-94783-7
// ISBN-10: 0-471-94783-0
// 1. Computer security. 2. Cryptography. I. Title.
// QA76.9.A25K638 2007
// 005.8--dc22 2006049338
// 15.9 THE MENEZES–VANSTONE ELLIPTIC CURVE CRYPTOSYSTEM, p. 443
// Another very significant source - "A Weakness of Menezes-Vanstone Cryptosystem", Klaus Kiefer, member of research group of prof. J. Buchmann, 1997
// Shortly, this work show ability of "known plain text attack (KPTA)", with probability O(1/p) of false detection.
// What does it mean? If we encrypt 128-bit session key, for success KPTA we must search in 2^128 combinations of session key ...
// Known plaintext attack for EC MV cryptosystem
// Curve E(p,a,b) known from public key,
// y0, y1, y2 - ciphertext
// random select 1 < x1 < p and 1< x2 < p
// calculate inversion a=inv(x1)(mod p), b=inv(x2)(mod p)
// c1=a*y1(mod p), c2=b*y2(mod p)
// if C(c1,c2) is point of curve E, x1 and x2 is plaintext with error probability O(1/p)
// z=((c1)^3 + a*c1 + b)(mod p)
// if (z^((p-1)/2))(mod p) == 1 there are 2 points (c1,+-c2) in curve E(p,a,b)
//#include <stdio.h>
#include <stdlib.h>
#include <string.h>
//#include <unistd.h>
#include <openssl/rand.h>
#define OPENSSL_NO_EC2M
#include <openssl/ec.h>
#define FORMATBIN 1
#define FORMATHEX 0
struct BinFmt192 // binary format of ECMV encrypted block, EC key = 192 bits
{
unsigned char y0[1+192/4]; // 2*24 byte BIGNUM + 1 header byte
unsigned char z1;
unsigned char y1[192/8]; // 24 bytes BIGNUM
unsigned char z2;
unsigned char y2[192/8]; // 24 bytes BIGNUM
unsigned char z3;
}; // size = 100 bytes
struct BinFmt224 // binary format of ECMV encrypted block, EC key = 224 bits
{
unsigned char y0[1+224/4]; // 2*28 byte BIGNUM + 1 header byte
unsigned char z1;
unsigned char y1[224/8]; // 28 bytes BIGNUM
unsigned char z2;
unsigned char y2[224/8]; // 28 bytes BIGNUM
unsigned char z3;
}; // size = 116 bytes
struct BinFmt256 // binary format of ECMV encrypted block, EC key = 256 bits
{
unsigned char y0[1+256/4]; // 2*32 byte BIGNUM + 1 header byte
unsigned char z1;
unsigned char y1[256/8]; // 32 bytes BIGNUM
unsigned char z2;
unsigned char y2[256/8]; // 32 bytes BIGNUM
unsigned char z3;
}; // size = 132 bytes
// Encrypt plaintext of length len with public EC key pubkey
// and store ciphertext in chipher = y0 (point), y1 (bignum), y2 (bignum)
// return error code, 0 if all OK
// Error codes:
/*
* 1 // no curve in key?
* 2 // wrong plaintext has odd length
* 3 // plaintext too long for this key
* 8 // binary format of encrypted block not defined for this key length
* 4 // internal error: ks is wrong, error in do-while
* 5 // internal error: error in EC_POINT_mul y0=ks*g
* 6 // internal error: error in EC_POINT_mul z=ks*q
* 7 // internal error: error EC_POINT_get_affine_coordinates_GFp
* errors 4,5,6,7 lead to memory leak :(
* */
// hex format:
// Encrypted text consist of 3 hex strings, each is ending with '\n'=0x0A
// First string has '04' header and two times longer than second and third
// length of encrypted block = 2*4*bits/8 + 5 = bits + 5 bytes, where 'bits' is length of EC key in bits
// Plaintext data MUST be of even length in bytes and not longer than 2*bits/8 = bits/4 bytes
// bin format:
// Encrypted text is binary block consist of 3 binary elements, each is ending with NULL=0x00 byte
// First element has 0x04 header and two times longer than second and third
// length of encrypted block in binary form = 4*bits/8 + 4 = bits/2 + 4 bytes, where 'bits' is length of EC key in bits
// In this example I used 192,224,256 bit EC keys and binary form for other key length don't supported :(
int EC_MV_pubkey_encrypt(unsigned char *cipher, EC_KEY* pubkey, unsigned char* plaintext, size_t len, int format);
// Decrypt with private EC key privkey ciphertext in cipher = y0 (point), y1 (bignum), y2 (bignum)
// and store result in plaintext
// return error code, 0 if all OK
/* Error codes:
* 1 // no curve in key?
* 8 // unknown format of ECMV encrypted block
* 9 // binary format of encrypted block not defined for this key length
* 10 // wrong format of binary encrypted block
* 2 // invalid hex point y0 representation
* 3,4 // wrong format of HEX encrypted data
* 11 // point y0 is not on curve
* 6 // internal error: error in EC_POINT_mul z=ks*q
* 7 // internal error: error EC_POINT_get_affine_coordinates_GFp
* errors 2,3,4,11,6,7 lead to memory leak :(
* */
int EC_MV_privkey_decrypt(unsigned char* cipher, EC_KEY *privkey, unsigned char* plaintext);
//
// DESCRIPTION 'EC Menezes-Vanstone cryptosystem functions openssl/Linux'
// COMPILER 'gcc (GCC) 4.8.2'
// FILE 'ecmv.c'
// AUTHOR "Nick Korepanov"
// Linux-3.10.104, glibc-2.17, OpenSSL 1.0.1u
// ECC-192/224/256
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
//
// Author of this program can be contacted by electronic mail
// korepanovnd@gmail.com
// Copyright (c) 2017 Nick Korepanov. All rights reserved.
// This product includes software developed by the OpenSSL Project
// for use in the OpenSSL Toolkit. (http://www.openssl.org/)
#include "ecmv.h"
int EC_MV_pubkey_encrypt(unsigned char *cipher, EC_KEY* pubkey, unsigned char* plaintext, size_t len, int format)
{
const EC_GROUP *curve; // curve, q and g are part of pubkey, it was allocated and free with pubkey
const EC_POINT *q;
EC_POINT *y0, *z;
BIGNUM *p, *a, *b, *ks, *o1, *z1, *z2, *y1, *y2, *x1, *x2, *ord;
int bits, i=0, err;
unsigned char buffer[250];
//size_t length;
BN_CTX *ctx;
curve=EC_KEY_get0_group(pubkey);
if(curve)
bits = EC_GROUP_get_degree(curve);
else
return 1; // no curve in key?
if(len%2)
return 2; // wrong plaintext has odd length
if(len > 2*bits/8)
return 3; // plaintext too long for this key
if( !(bits == 192 || bits == 224 || bits == 256) && format)
return 8; // binary format of encrypted block not defined for this key length
//prepare bignums
p=BN_new(); a=BN_new(); b=BN_new(); ks=BN_new(); o1=BN_new(); z1=BN_new(); z2=BN_new(); y1=BN_new(); y2=BN_new(); x1=BN_new(); x2=BN_new(); ord=BN_new();
ctx=BN_CTX_new();
//prepare points
//q=EC_POINT_new(curve); g=EC_POINT_new(curve);
y0=EC_POINT_new(curve); z=EC_POINT_new(curve);
// split plaintext at two parts, and assign it to BIGNUMs
BN_bin2bn(plaintext, len/2, x1);
BN_bin2bn(plaintext+len/2, len/2, x2);
// get public key q
q=EC_KEY_get0_public_key(pubkey);
// get generator point g
//g=EC_GROUP_get0_generator(curve);
// get order of g
EC_GROUP_get_order(curve, ord, ctx );
// get prime p
EC_GROUP_get_curve_GFp(curve, p, a, b, ctx );
BN_sub(o1, ord, BN_value_one()); // o1=ord-1
do
{
if( i>= 10)
break;
// make secret session key ks > 1 and ks < (o-1)
RAND_bytes(buffer, bits/8);
BN_bin2bn(buffer, bits/8, ks);
i++;
}
while( BN_cmp(BN_value_one(), ks) >=0 || BN_cmp(o1,ks) <=0 );
if(i>=10)
return 4; // ks is wrong, error in do-while
// y0=ks*g
err=EC_POINT_mul(curve, y0, ks, NULL, NULL, ctx );
if(err == 0)
return 5; // error in EC_POINT_mul y0=ks*g
// z=ks*q
err=EC_POINT_mul(curve, z, NULL, q, ks, ctx );
if(err == 0)
return 6; // error in EC_POINT_mul z=ks*q
// get z1,z2 = Z(z1,z2)
err=EC_POINT_get_affine_coordinates_GFp(curve, z, z1, z2, ctx );
if(err == 0)
return 7; //error EC_POINT_get_affine_coordinates_GFp
//y1 = z1*x1(modulo p)
//y2 = z2*x2(modulo p)
BN_mod_mul(y1, z1, x1, p, ctx);
BN_mod_mul(y2, z2, x2, p, ctx);
/* if bits=192, 24 bytes per every BIGNUM, point contains 2 Bignum + 1 byte header */
if(format)
{ // bin format
if(bits == 192)
{
struct BinFmt192 *out;
out=(struct BinFmt192 *)cipher;
EC_POINT_point2oct(curve, y0, POINT_CONVERSION_UNCOMPRESSED, (unsigned char*)&out->y0, sizeof(out->y0), ctx);
BN_bn2bin(y1, (unsigned char*)&out->y1);
BN_bn2bin(y2, (unsigned char*)&out->y2);
out->z1=out->z2=out->z3=0;
}
if(bits == 224)
{
struct BinFmt224 *out;
out=(struct BinFmt224 *)cipher;
EC_POINT_point2oct(curve, y0, POINT_CONVERSION_UNCOMPRESSED, (unsigned char*)&out->y0, sizeof(out->y0), ctx);
BN_bn2bin(y1, (unsigned char*)&out->y1);
BN_bn2bin(y2, (unsigned char*)&out->y2);
out->z1=out->z2=out->z3=0;
}
if(bits == 256)
{
struct BinFmt256 *out;
out=(struct BinFmt256 *)cipher;
EC_POINT_point2oct(curve, y0, POINT_CONVERSION_UNCOMPRESSED, (unsigned char*)&out->y0, sizeof(out->y0), ctx);
BN_bn2bin(y1, (unsigned char*)&out->y1);
BN_bn2bin(y2, (unsigned char*)&out->y2);
out->z1=out->z2=out->z3=0;
}
}
else
{ // hex format
strcpy((char*)cipher, EC_POINT_point2hex(curve, y0, POINT_CONVERSION_UNCOMPRESSED, ctx));
strcat((char*)cipher, "\n");
strcat((char*)cipher, BN_bn2hex(y1));
strcat((char*)cipher, "\n");
strcat((char*)cipher, BN_bn2hex(y2));
strcat((char*)cipher, "\n");
}
// free points
//EC_POINT_free(q); EC_POINT_free(g);
EC_POINT_free(y0); EC_POINT_clear_free(z);
BN_CTX_free(ctx);
BN_clear(ks);
BN_clear(x1);
BN_clear(x2);
BN_clear(z1);
BN_clear(z2);
//free bignums
BN_free(p); BN_free(a); BN_free(b); BN_free(ks); BN_free(o1); BN_free(z1); BN_free(z2); BN_free(y1); BN_free(y2); BN_free(x1); BN_free(x2); BN_free(ord);
return 0;
}
int EC_MV_privkey_decrypt(unsigned char* cipher, EC_KEY *privkey, unsigned char* plaintext)
{
const EC_GROUP *curve; // curve, d are part of privkey, it was allocated and free with privkey
const BIGNUM *d;
EC_POINT *y0, *z;
BIGNUM *p, *a, *b, *z1, *z2, *y1, *y2, *x1, *x2;
int err, bits, format;
unsigned char *ptr;
BN_CTX *ctx;
ctx=BN_CTX_new();
curve=EC_KEY_get0_group(privkey);
if(!curve)
return 1; // no curve in key?
bits = EC_GROUP_get_degree(curve);
if( cipher[0] == 0x04 )
format=FORMATBIN;
if( cipher[0] == 0x30 )
format=FORMATHEX;
if(cipher[0] != 0x04 && cipher[0] != 0x30)
return 8; // unknown format of ECMV encrypted block
if( !(bits == 192 || bits == 224 || bits == 256) && format)
return 9; // binary format of encrypted block not defined for this key length
if(format && bits == 192 && (cipher[48+1] || cipher[48+1+24+1] || cipher[48+1+24+1+24+1] ))
return 10; //wrong format of binary encrypted block
if(format && bits == 224 && (cipher[56+1] || cipher[56+1+28+1] || cipher[56+1+28+1+28+1] ))
return 10; //wrong format of binary encrypted block
if(format && bits == 256 && (cipher[64+1] || cipher[64+1+32+1] || cipher[64+1+32+1+32+1] ))
return 10; //wrong format of binary encrypted block
//prepare bignums
p=BN_new(); a=BN_new(); b=BN_new(); z1=BN_new(); z2=BN_new(); y1=BN_new(); y2=BN_new(); x1=BN_new(); x2=BN_new();
//prepare points
y0=EC_POINT_new(curve); z=EC_POINT_new(curve);
// get private key d
d=EC_KEY_get0_private_key(privkey);
// get prime p
EC_GROUP_get_curve_GFp(curve, p, a, b, ctx);
if(format)
{
if(bits == 192)
{
struct BinFmt192 *in;
in=(struct BinFmt192 *)cipher;
EC_POINT_oct2point(curve, y0, (const unsigned char *)&in->y0, sizeof(in->y0), ctx);
BN_bin2bn((const unsigned char *)&in->y1, sizeof(in->y1), y1);
BN_bin2bn((const unsigned char *)&in->y2, sizeof(in->y2), y2);
}
if(bits == 224)
{
struct BinFmt224 *in;
in=(struct BinFmt224 *)cipher;
EC_POINT_oct2point(curve, y0, (const unsigned char *)&in->y0, sizeof(in->y0), ctx);
BN_bin2bn((const unsigned char *)&in->y1, sizeof(in->y1), y1);
BN_bin2bn((const unsigned char *)&in->y2, sizeof(in->y2), y2);
}
if(bits == 256)
{
struct BinFmt256 *in;
in=(struct BinFmt256 *)cipher;
EC_POINT_oct2point(curve, y0, (const unsigned char *)&in->y0, sizeof(in->y0), ctx);
BN_bin2bn((const unsigned char *)&in->y1, sizeof(in->y1), y1);
BN_bin2bn((const unsigned char *)&in->y2, sizeof(in->y2), y2);
}
}
else
{
// read y0
ptr=cipher;
y0=EC_POINT_hex2point(curve, (const char *)ptr, y0, ctx);
if(y0 == NULL)
return 2; //invalid hex point representation
//read y1,y2
ptr=strchr((const char *)ptr,'\n');
if(ptr == NULL)
return 3; //wrong format of encrypted data
ptr++;
BN_hex2bn(&y1, (const char *)ptr);
ptr=strchr((const char *)ptr,'\n');
if(ptr == NULL)
return 4; //wrong format of encrypted data
ptr++;
BN_hex2bn(&y2, (const char *)ptr);
}
if( !EC_POINT_is_on_curve(curve, (const EC_POINT *)y0, ctx) )
return 11; // point is not on curve
// z=d*y0=d*ks*g=ks*q
err=EC_POINT_mul(curve, z, NULL, y0, d, ctx );
if(err == 0)
return 6; // error in EC_POINT_mul z=ks*q
// get z1,z2 = Z(z1,z2)
err=EC_POINT_get_affine_coordinates_GFp(curve, z, z1, z2, ctx );
if(err == 0)
return 7; //error EC_POINT_get_affine_coordinates_GFp
// a=inv(z1)(mod p)
BN_mod_inverse(a, z1, p, ctx);
// b=inv(z2)(mod p)
BN_mod_inverse(b, z2, p, ctx);
//x1 = a*y1(modulo p)
//x2 = b*y2(modulo p)
BN_mod_mul(x1, a, y1, p, ctx);
BN_mod_mul(x2, b, y2, p, ctx);
// decode plaintext from two parts
BN_bn2bin(x1, plaintext);
BN_bn2bin(x2, plaintext+BN_num_bytes(x1));
// free points
EC_POINT_free(y0); EC_POINT_clear_free(z);
BN_CTX_free(ctx);
BN_clear(x1);
BN_clear(x2);
BN_clear(z1);
BN_clear(z2);
BN_clear(a);
BN_clear(b);
//free bignums
BN_free(p); BN_free(a); BN_free(b); BN_free(z1); BN_free(z2); BN_free(y1); BN_free(y2); BN_free(x1); BN_free(x2);
return 0;
}
Спасибо за внимание!
Поделиться с друзьями
Комментарии (19)
YourChief
12.07.2017 21:33+1Вейершрасса
Вейерштрасса
над полями целых чисел, определенных как Zp, где Zp — множество целых чисел, меньших некоторого простого числа р и больших нуля
равенство нулю допускается
Исходники в виде листинга в статье — это крайне неудобно, поверьте.
Я считаю, что Elliptic curve Menezes-Vanstone cryptosystem совершенно незаслуженно забыта.
Совершенно заслуженно, существует ElGamal для ECC и ECIES. В частности, даже современный GPG позволяет генерировать ключи ECC и шифровать данные с помощью ECDH.NickKorepanov
13.07.2017 08:11Вейерштрасса
Спасибо, исправил.
Совершенно заслуженно, существует ElGamal для ECC и ECIES. В частности, даже современный GPG позволяет генерировать ключи ECC и шифровать данные с помощью ECDH.
Мне показалось несправедливым, что для RSA и ElGamal есть алгоритмы шифрования на публичном ключе и расшифровки на приватном, а для ECC такой простейшей схемы нет.
medico_della_peste
13.07.2017 14:21Реализация ECC шифрования в libgcrypt есть, но очень специфична. Если коротко, то шифруемое сообщение m отображается на точку эллиптической кривой mG
Отображение сообщения/текста на точки кривой это очень нетривиальная задача, именно поэтому нет практической реализации ElGamal для EC, хотя теоретически такой алгоритм существует.
В libgcrypt есть протоколы для обмена ключами (ECDH) и создания цифровой подписи (ECDSA), в них речь не о шифровании сообщений все-таки.NickKorepanov
13.07.2017 14:35В libgcrypt есть протоколы для обмена ключами (ECDH) и создания цифровой подписи (ECDSA), в них речь не о шифровании сообщений все-таки.
Простите мне мою безграмотность, но все-таки в libgcrypt есть функции gcry_pk_encrypt и gcry_pk_decrypt,
которые, будучи применены с ключами RSA или ElGamal шифруют и расшифровывают, т.е. ведут себя ожидаемо и в соответствии с документацией. Однако, если ключ ECC, то при шифровании открытый текст «012345678» превращается в шифротекст из двух элементов (точек), и это похоже на норму, а вот дешифровка дает большой бинарный блок с префиксом 0x04, т.е. точку… Или я чего-то не понимаю? В общем, libgcrypt меня разочаровал.medico_della_peste
13.07.2017 15:29gcry_pk_encrypt требует публичного ключа, gcry_pk_decrypt — приватного. RSA и ElGamal генерируют эти пары ключей. Как Вы получаете эту пару используя EC?
NickKorepanov
13.07.2017 16:47Как Вы получаете эту пару используя EC
Как обычно, gcry_pk_genkey с параметром "(genkey (ecc (curve \«secp192r1\») ))"
В результате появляются приватный и публичный ключи ECC. Это все работает правильно, претензий нет.
medico_della_peste
13.07.2017 18:26ECC ключи должны использоваться ECDSA/ECDH алгоритмами. А какие параметры Вы задаете в gcry_pk_encrypt?
NickKorepanov
13.07.2017 20:30ECC ключи должны использоваться ECDSA/ECDH алгоритмами
Ну, если gcry_pk_algo_info сообщает про то, что алгоритм ecc может GCRY_PK_USAGE_ENCR, то я просто вынужден в это поверить.
А какие параметры Вы задаете в gcry_pk_encrypt?
"(data (flags raw) (value #01020304050607080910111213141516#))" и public key.
Шифрование проходит без ошибок. В результате появляется шифротекст из двух элементов (по виду заголовков 0x04 — точек)medico_della_peste
13.07.2017 22:19+1Сложно это обсуждать не видя код. То, что Вы получаете похоже на формат описанный в пункте 6 (Conversion Primitives) здесь: https://tools.ietf.org/html/rfc6637#page-4. Префикс 04 и дальше конкатенация двух координат.
ECDH сам по себе не шифрует сообщение, он формирует ключ, который в дальнейшем используется как симметричный ключ в AES.NickKorepanov
14.07.2017 08:20-1ECDH
Опять, зачем обсуждать протоколы, когда обсуждаемая статья посвящена алгоритмам асимметричного шифрования, и конкретно — отсутствию реализации алгоритма шифрования на публичном ключе и расшифровки на приватном для ECC. Как следует из статьи, описание такого алгоритма было дано более 20 лет назад, но почему-то не попало ни в openssl, ни в libgcrypt. Вот это странно. И что плохого в предложенном алгоритме ECMV, мне тоже непонятно. По сравнению со своими аналогами RSA и ElGamal он мало в чем им уступает, как мне кажется.medico_della_peste
14.07.2017 14:17+1что плохого в предложенном алгоритме ECMV, мне тоже непонятно
Если мы строим элиптическую кривую над полем Fq, где q состоит из s десятичных знаков, то каждый блок сообщения будет состоять из 2s ln 10 ? 6.64s битов. В ASCII (8 битов на символ) мы получаем 0.83s символов в блоке. Предположим энтропия английского языка равна 1.3 бита на символ. Тогда для перебора нам нужно 2?1.3*0.83s ? 2?s операций. При сравнительно небольшом s это вполне возможно.
imbasoft
Не совсем понятное утверждение. Diffe-Hellman подобное может. Например, RFC4357 для отечественных ГОСТов описывает механизм способы применения DH — для защиты сессионных ключей.
NickKorepanov
ECDH работает с двумя публичными ключами, стороны А и стороны Б, из двух публичных ключей получается общий секрет. В своей статье я попытался найти классический вариант с одним ключом — шифрование на публичном ключе, расшифровка на приватном, описано, что нашел и как реализовал.
YourChief
При таком четырёхкратном оверхеде, как в этой схеме, получается выгоднее просто открытый ключ к каждому сообщению прикладывать и использовать ECDH.
NickKorepanov
Ну, это… как-бы надо сравнивать подобное с подобным, при равной криптостойкости.
Возьмем RSA-2048: размер шифротекста 256 байт, размер шифруемых данных менее 256 байт
Возьмем ElGamal-2048: размер шифротекста 512 байт, размер шифруемых данных менее 256 байт
ECMV-224: размер шифротекста 112 байт, размер шифруемых данных менее 56 байт…
Если ECMV даже по относительному оверхеду ничем не хуже ElGamal, в чем вопрос?
И это даже без компрессии точек (а можно сохранять только одну координату x), которая, будучи применена, может уменьшить размер шифротекста еще на 1 модуль… тогда размер шифротекста будет 3 модуля, при размере шифруемых данных 2 модуля.
Про абсолютные размеры шифротекстов я уже молчу.
YourChief
Возьмём ECDH на Curve25519. Основные её реализации используют только одну координату. Таким образом размер шифрованного текста равен 32м байтам открытого ключа + размер шифрованного блочным шифром текста. Это оставляет далеко позади схему ECMV по всем параметрам и реализовано во множестве криптографических библиотек. Пример — libsodium.
NickKorepanov
Мне по своей темности непонятно, зачем менять тему обсуждения и обсуждать протоколы, когда обсуждаемая статья посвящена алгоритмам асимметричного шифрования, и конкретно — отсутствию реализации алгоритма шифрования на публичном ключе и расшифровки на приватном для ECC.
YourChief
Раз уж Вы решили спекулировать терминами, то для шифрования сообщения необходима криптосистема, которая является чем-то большим, чем алгоритм. В своей статье Вы сами называете ECMV криптосистемой:
Существуют криптосистемы, которые решают конкретно эту задачу и конкретно на эллиптических кривых гораздо лучше: https://en.wikipedia.org/wiki/Integrated_Encryption_Scheme
Её особенности:
Выходит, что для обмена шифрованными сообщениями с помощью ECC, гораздо выгоднее использовать ECC для выведения общего секрета, вместо того, чтобы пытаться зашифровать им какой-то произвольный общий секрет.
Вот по этим практическим соображениям ECMV отправился на свалку истории, а ECIES и его вариации стали стандартом. Эту мысль я и пытаюсь до Вас донести.
NickKorepanov
Ее так называют в литературе. И система эта очень напоминает RSA или ElGamal, которым Вы, я надеюсь, не отказываете в праве также являться криптосистемами.
RSA, кстати, также как и ECMV подвержен атаке с угадываением открытого текста, так как шифрование RSA является полностью детерминированным (шифротекст зависит только от открытого текста и ключа), и зная публичный ключ, можно перебирать варианты открытого текста, шифровать на публичном ключе и искать совпадение полученного шифротекста с имеющимся. Однако наличие такой «проблемы» нисколько не мешает криптосистеме RSA быть повсеместно реализованной и успешно работать чуть ли не в любой железке.
И нужно-то от такой системы очень немного: Уметь шифровать на публичном ключе, расшифровывать на приватном, уметь делать подпись на приватном ключе и проверять ее на публичном.
То, что Вы упорно пытаетесь подменить тему обсуждения и перейти к протоколам, сторонам, парам ключей, о чем в моей статье нет ни слова (это другой уровень), на мой взгляд — достойно сожаления. Но в любом случае — спасибо Вам за внимание и высказанные Вами мнения, даже если я с ними и не согласен.