Подписание с помощью Curve25519 в Crypto ++ - PullRequest
0 голосов
/ 04 марта 2019

Я внедряю Curve25519 в одном из моих проектов.Я думал, что могу объединить это с HMAC, CMAC или другим алгоритмом, чтобы подписать и проверить.Проблема в том, что Curve25519 не генерирует одни и те же общие ключи.

Я не очень разбираюсь в криптографии и не знаю, делаю ли я что-то не так или просто не могу объединить Curve25519 с HMAC.

Вот код теста, который я готовлю.

#include "xed25519.h"
using CryptoPP::x25519;
#include "donna.h"
using CryptoPP::Donna::curve25519_mult;
using CryptoPP::Donna::ed25519_sign;
using CryptoPP::Donna::ed25519_sign_open;
using CryptoPP::Donna::ed25519_publickey;

#include "filters.h"
#include "osrng.h"

#include "cryptlib.h"
#include "files.h"
#include "hex.h"
#include "sha.h"
#include "hmac.h"
using namespace std;

static const int SECRET_KEYLENGTH=32;

static const int PRIVATE_KEYLENGTH=128;
static const int PUBLIC_KEYLENGTH=32;
#include <string>
#include <iostream>

int main(int argc, char* argv[])
{    
    using namespace CryptoPP;

    AutoSeededRandomPool prng, prng2;
    byte *aux ;// new byte[PRIVATE_KEYLENGTH];
    byte privateKey[PUBLIC_KEYLENGTH];
    byte publicKey[PUBLIC_KEYLENGTH];

    // Node 1
    x25519 x(privateKey, publicKey) ; //(   const byte  y[PUBLIC_KEYLENGTH],const byte  x[SECRET_KEYLENGTH] 

    cout << "1- Generating private key " << endl;
    aux = new byte;

    x.GeneratePrivateKey(prng, privateKey);
    Integer intPrvK(privateKey, sizeof(privateKey));
    cout << "Private key created " <<  intPrvK  << endl;

    cout << "1- Generating public key " << endl;
    //void  GeneratePublicKey (RandomNumberGenerator &rng, const byte *privateKey, byte *publicKey) const
    if(curve25519_mult(privateKey, publicKey) == 0 ){
        Integer intPubK(publicKey, sizeof(publicKey));
        cout << "1- Public key created " <<  intPubK  << endl;
        //cout << "1- The new public key is " << privateKey << endl;
    }
    else
        cout << "curve25519_mult did not work " << endl;


    //Node 2
    byte privateKey2[PUBLIC_KEYLENGTH];
    byte publicKey2[PUBLIC_KEYLENGTH];
    x25519 y(privateKey2, publicKey2) ; //( const byte  y[PUBLIC_KEYLENGTH],const byte  x[SECRET_KEYLENGTH] 
    cout << "2- Generating private key " << endl;
    aux = new byte;

    y.GeneratePrivateKey(prng2, privateKey2);
    Integer intPrvK2(privateKey2, sizeof(privateKey2));
    cout << "2- Private key created " <<  intPrvK2  << endl;

    cout << "2- Generating public key " << endl;
    //void  GeneratePublicKey (RandomNumberGenerator &rng, const byte *privateKey, byte *publicKey) const
    if(curve25519_mult(privateKey2, publicKey2) == 0 ){
        Integer intPubK2(publicKey2, sizeof(publicKey2));
        cout << "2- Public key created " <<  intPubK2  << endl;
        //cout << "2- The new public key is " << privateKey2 << endl;
    }
    else
        cout << "2- curve25519_mult did not work " << endl;            

    cout << "\nGenerations of shared keys" << endl; 
    /*int curve25519_mult   (   byte    sharedKey[32],
        const byte  secretKey[32],
        const byte  othersKey[32] 
        )   */
    byte sharedKey1_2[PUBLIC_KEYLENGTH];
    byte sharedKey2_1[PUBLIC_KEYLENGTH];
    if( curve25519_mult(sharedKey1_2, privateKey, publicKey2) == 0){
        Integer intSharedKey1_2(sharedKey1_2, sizeof(sharedKey1_2));
        cout << "1- Shared key created " << intSharedKey1_2 << endl;
    }

    if( curve25519_mult(sharedKey2_1, privateKey2, publicKey) == 0){
        Integer intSharedKey2_1(sharedKey2_1, sizeof(sharedKey2_1));
        cout << "2- Shared key created " << intSharedKey2_1 << endl;
    }

    // We have two keys each.

    string plain = "\n\nHMAC Test";
    string mac, encoded;

    /*********************************\
    \*********************************/
    cout << "plain text: " << plain << endl;

    /*********************************\
    \*********************************/

    try
    {
        HMAC< SHA256 > hmac(sharedKey1_2, PUBLIC_KEYLENGTH);

        StringSource ss2(plain, true, 
            new HashFilter(hmac,
                new StringSink(mac)
            ) // HashFilter      
        ); // StringSource
    }
    catch(const CryptoPP::Exception& e)
    {
        cerr << e.what() << endl;
        exit(1);
    }

    /*********************************\
    \*********************************/

    // Pretty print
    encoded.clear();
    StringSource ss3(mac, true,
        new HexEncoder(
            new StringSink(encoded)
        ) // HexEncoder
    ); // StringSource

    cout << "hmac: " << encoded << endl;

    try
    {
        HMAC< SHA256 > hmac2(sharedKey2_1, PUBLIC_KEYLENGTH);
        const int flags = HashVerificationFilter::THROW_EXCEPTION | HashVerificationFilter::HASH_AT_END;

        StringSource(plain + mac, true, 
            new HashVerificationFilter(hmac2, NULL, flags)
        ); // StringSource

        cout << "Verified message" << endl;
    }
    catch(const CryptoPP::Exception& e)
    {
        cerr << e.what() << endl;

    }
    return 0;
}

РЕДАКТИРОВАТЬ: Вот результат этого кода:

1- Generating private key 
Private key created 3951427468589058657788500055898583055730859037456996206614247149081707227760.
1- Generating public key 
1- Public key created 2713877106980505211026290261997698325438191786766062178625865092937394618368.
2- Generating private key 
2- Private key created 58089620826126204922773651760985512282935010454438059044416143831910823682427.
2- Generating public key 
2- Public key created 1185077373537344710091841384487531087158005785833397747712.

Generations of shared keys
1- Shared key created 32717475549536125870454478996763331991259932599267432219938737089203052157444.
2- Shared key created 83438083910146518364399797164490155462911710345063602550172142504835353991253.
plain text: 

HMAC Test
hmac: 27C84FED802319639DF86D36E43090666D6CB20F556778B90819087BC55C2249
HashVerificationFilter: message hash or MAC not valid

Я надеюсь, что любой из вас можетобъясни мне.

Заранее спасибо !!

Ответы [ 2 ]

0 голосов
/ 08 марта 2019

Наконец, я получил решение благодаря ответу jww.Проблема заключалась в том, что, как я и ожидал, при генерации ключей, возможно, в коде отсутствовала согласованная часть между ключами, хотя я точно не знаю, как решить проблему с предыдущими функциями.

Здесь естьПример рабочего кода, реализующего подпись HMAC с обменом ключами x25519.

//g++ -g3 -ggdb -O0  Curve25519_HMAC_2.cpp -o Curve25519_HMAC_2.exe -lpthread -I/usr/local/include/cryptopp -L/usr/local/lib -l cryptopp
//g++ -DNDEBUG -g -g3 -O2 -Wall -Wextra -o Curve25519_HMAC_2  Curve25519_HMAC_2.cpp -I/usr/local/include/cryptopp -L/usr/local/lib -l cryptopp


#include "xed25519.h"
using CryptoPP::x25519;
#include "donna.h"
using CryptoPP::Donna::curve25519_mult;
using CryptoPP::Donna::ed25519_sign;
using CryptoPP::Donna::ed25519_sign_open;
using CryptoPP::Donna::ed25519_publickey;

#include "filters.h"
#include "osrng.h"

#include "cryptlib.h"
#include "files.h"
#include "hex.h"
#include "sha.h"
#include "hmac.h"
using namespace std;


static const int SECRET_KEYLENGTH=32;
static const int PRIVATE_KEYLENGTH=128;
static const int PUBLIC_KEYLENGTH=32;
#include <string>
#include <iostream>

int main(int argc, char* argv[])
{

    using namespace CryptoPP;


    AutoSeededRandomPool rndA, rndB;
    x25519 ecdhA(rndA), ecdhB(rndB);

    SecByteBlock privA(ecdhA.PrivateKeyLength());
    SecByteBlock pubA(ecdhA.PublicKeyLength());
    ecdhA.GenerateKeyPair(rndA, privA, pubA);

    SecByteBlock privB(ecdhB.PrivateKeyLength());
    SecByteBlock pubB(ecdhB.PublicKeyLength());
    ecdhB.GenerateKeyPair(rndB, privB, pubB);

    SecByteBlock sharedA(ecdhA.AgreedValueLength());
    SecByteBlock sharedB(ecdhB.AgreedValueLength());

    if(!ecdhA.Agree(sharedA, privA, pubB))
         throw std::runtime_error("Failed to reach shared secret (1)");

    if(!ecdhB.Agree(sharedB, privB, pubA))
        throw std::runtime_error("Failed to reach shared secret (2)");

    HexEncoder encoder(new FileSink(std::cout));

    std::cout << "Shared secret (A): ";
    StringSource(sharedA, sharedA.size(), true, new Redirector(encoder));
    std::cout << std::endl;

    std::cout << "Shared secret (B): ";
    StringSource(sharedB, sharedB.size(), true, new Redirector(encoder));
    std::cout << std::endl;



    // We have two keys each.


    string plain = "\n\nHMAC Test";
    string mac, encoded;

    /*********************************\
    \*********************************/
    cout << "plain text: " << plain << endl;

    /*********************************\
    \*********************************/

    try
    {
        HMAC< SHA256 > hmac(sharedA, sharedA.size());

        StringSource ss2(plain, true, 
            new HashFilter(hmac,
                new StringSink(mac)
            ) // HashFilter      
        ); // StringSource
    }
    catch(const CryptoPP::Exception& e)
    {
        cerr << e.what() << endl;
        exit(1);
    }

    /*********************************\
    \*********************************/

    // Pretty print
    encoded.clear();
    StringSource ss3(mac, true,
        new HexEncoder(
            new StringSink(encoded)
        ) // HexEncoder
    ); // StringSource

    cout << "hmac: " << encoded << endl;


    try
    {
        HMAC< SHA256 > hmac2(sharedB, sharedB.size());
        const int flags = HashVerificationFilter::THROW_EXCEPTION | HashVerificationFilter::HASH_AT_END;

        StringSource(plain + mac, true, 
            new HashVerificationFilter(hmac2, NULL, flags)
        ); // StringSource

        cout << "Verified message" << endl;
    }
    catch(const CryptoPP::Exception& e)
    {
        cerr << e.what() << endl;

    }

    return 0;
}

А вот вывод:

Shared secret (A): 284FE14022541BD8939C40249A3805DB6C4548B01FF0826253E6FAC53C489D46
Shared secret (B): 284FE14022541BD8939C40249A3805DB6C4548B01FF0826253E6FAC53C489D46
plain text: 

HMAC Test
hmac: BCFEE5E6CCA6EB9818D961DA22545CE9989E799430AA54E9EDBEF35A244D4C77
Verified message
0 голосов
/ 05 марта 2019

Проблема в том, что Curve25519 не генерирует одинаковые общие ключи ...

curve25519 описывает базовое поле.x25519 - ключевое соглашение по области.Каждый запуск протокола создает новый общий секрет.Это происходит потому, что каждый раз, когда запускается протокол, используется новый набор случайных параметров.

Вот как вы выполняете согласование ключей с x25519 .x25519 - это схема согласования ключей Бернштейна с использованием кривой25519.Пример кода ниже был взят из Crypto ++ wiki .

Сначала создайте несколько эфемерных ключей:

AutoSeededRandomPool rndA, rndB;
x25519 ecdhA(rndA), ecdhB(rndB);

SecByteBlock privA(ecdhA.PrivateKeyLength());
SecByteBlock pubA(ecdhA.PublicKeyLength());
ecdhA.GenerateKeyPair(rndA, privA, pubA);

SecByteBlock privB(ecdhB.PrivateKeyLength());
SecByteBlock pubB(ecdhB.PublicKeyLength());
ecdhB.GenerateKeyPair(rndB, privB, pubB);

Во-вторых, настройте общий секретный буфер:

SecByteBlock sharedA(ecdhA.AgreedValueLength());
SecByteBlock sharedB(ecdhB.AgreedValueLength());

В-третьих, выполните протокол соглашения:

if(!ecdhA.Agree(sharedA, privA, pubB))
    throw std::runtime_error("Failed to reach shared secret (1)");

if(!ecdhB.Agree(sharedB, privB, pubA))
    throw std::runtime_error("Failed to reach shared secret (2)");

Наконец, вы можете проверить ключи:

HexEncoder encoder(new FileSink(std::cout));

std::cout << "Shared secret (A): ";
StringSource(sharedA, sharedA.size(), true, new Redirector(encoder));
std::cout << std::endl;

std::cout << "Shared secret (B): ";
StringSource(sharedB, sharedB.size(), true, new Redirector(encoder));
std::cout << std::endl;

Пример вывода похож на следующий.

$ ./test.exe
Shared secret (A): B5C105BC3B685869AFBDFE64F15D27D6D0EAAA1A22F03B45B86E09FC76522450
Shared secret (B): B5C105BC3B685869AFBDFE64F15D27D6D0EAAA1A22F03B45B86E09FC76522450

* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * имеет много ручных отказов.Вам все еще нужно отправить открытую часть эфемерного ключа другой стороне.И эфемерный открытый ключ, отправленный другой стороне, должен быть подписан, чтобы другая сторона знала, что он является подлинным.


... для подписи и проверки ...

Вот как вы подписываетесь с помощью ed25519 схемы подписи.ed25519 - это схема подписи Бернштейна с использованием кривой25519.Приведенный ниже пример кода был взят из Crypto ++ wiki .

. Сначала создайте ключ подписи:

ed25519::Signer signer;
signer.AccessPrivateKey().GenerateRandom(prng);

Во-вторых, сохраните ключ подписи:

FileSink fs("private.key.bin");
signer.GetPrivateKey().Save(fs);

В-третьих, подпишите сообщение с помощью закрытого ключа:

AutoSeededRandomPool prng;
HexEncoder encoder(new FileSink(std::cout));

std::string message = "Yoda said, Do or do not. There is no try.";
std::string signature;

// Determine maximum signature size
size_t siglen = signer.MaxSignatureLength();
signature.resize(siglen);

// Sign, and trim signature to actual size
siglen = signer.SignMessage(prng, (const byte*)&message[0], message.size(), (byte*)&signature[0]);
signature.resize(siglen);

// Print signature to stdout
std::cout << "Signature: ";
StringSource(signature, true, new Redirector(encoder));
std::cout << "\n" << std::endl;

Пример вывода выглядит следующим образом.

$ ./test.exe
Signature: B8EABDAA754BBCDC0B11ADE1FBA52CE39CD52FF42DE95E44CA6103652171468B63446
81DFB09F0D556EBF01BE43064D90C76711D9E1FF0FD3C41AF843DF17909

Вы можете сохранить свой открытый ключсо следующим кодом.Затем передайте ваш открытый ключ другим людям.

ed25519::Signer signer;
...

ed25519::Verifier verifier(signer);

FileSink fs("public.key.bin");
verifier.GetPublicKey().Save(fs);

... Я думал, что смогу совместить это с HMAC, CMAC или другим алгоритмом [для подписи] ...

Я не уверен, что делать с схемой, которую вы предлагаете, или с использованием HMAC и CMAC.Как отмечает @Maarten в комментариях, вы не описываете, что вы пытаетесь сделать, и не излагаете алгоритм.Я собираюсь оставить это в покое.

Обычно в вашем случае использования вы получаете общий секрет, используя Диффи-Хеллмана.Затем вы получаете пару ключей для блочного шифра или потокового шифра и MAC.Вы обычно делаете это с чем-то вроде HDKF.Наконец, вы вводите шифр и макинтош, а затем выполняете массовое шифрование.

Я рискну предположить, что, как только вы получите общий секрет с ecdhA.Agree(sharedA, privA, pubB) и / или ecdhB.Agree(sharedB, privB, pubA), получите ключ с помощьюHKDF, а затем используйте полученный ключ для ключа ChaCha20Poly1305 или XChaCha20Poly1305 (или другого шифрования с проверкой подлинности * шифра режима 1081 *)

При использовании ChaCha20Poly1305 или XChaCha20Poly1305 каждое сообщение должно (обязательно!) Получать уникальный одноразовый номер.Просто запустите счетчик и увеличивайте его после каждого сообщения.

...