Реализация Elliptic curve Menezes-Vanstone cryptosystem на базе OpenSSL API

в 11:29, , рубрики: elliptic curve cryptography, openssl, public key, криптография, Программирование, метки:

Здравствуйте, уважаемыее! По мере моего посильного занятия криптографией для своих скромных нужд, в попытках поддержать достойный уровень безопасности данных (я ориентируюсь на уровни, указанные в разделе 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]
Разберем этот вопрос подробнее.

Немного математики:

Для простоты будем говорить только о эллиптических кривых, вид которых описывается уравнением Вейершрасса $inline$y^2=x^3+ax+b$inline$ над полями целых чисел, определенных как 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;
}

Спасибо за внимание!

Автор: NickKorepanov

Источник

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js