4 votes

Comment combiner deux sources en une nouvelle dans Crypto++ ?

Situation

J'ai deux sources arbitraires, disons une StringSource d'un signature et un FileSource à partir des dossier signé . Je veux maintenant vérifier la signature des fichiers, ce qui est actuellement effectué de la manière suivante :

bool VerifyFile(const ECDSA<ECP, SHA512>::PublicKey &key,
                const std::string &filename,
                const std::string &signatureString) {
    std::string fileContentString;
    FileSource(filename.c_str(), true,
               new CryptoPP::StringSink(fileContentString));

    bool result = false;
    StringSource(signatureString + fileContentString, true,
                 new SignatureVerificationFilter(
                         ECDSA<ECP, SHA512>::Verifier(key),
                         new ArraySink((byte *) &result, sizeof(result))
                 ) // SignatureVerificationFilter
    );
    return result;
}

Mon problème

Je ne veux pas extraire explicitement le contenu du fichier dans une chaîne, puis faire une concaténation et vérifier ensuite.

Pregunta

Existe-t-il un moyen de transmettre à l'entité de vérification deux sources arbitraires dont l'une représente la signature et l'autre le contenu signé (qui peut être un fichier ou une chaîne) ?

Ce que j'ai essayé jusqu'à présent

J'ai essayé Source::TransferAll(...) à un Redirecter redirigeant vers SignatureVerificationFilter sans succès.

1voto

jww Points 9514

J'ai deux sources arbitraires, disons une StringSource à partir d'une signature et une FileSource à partir du fichier signé correspondant. Je veux maintenant vérifier la signature des fichiers ...

L'utilisation de plusieurs sources sur la même chaîne de filtrage peut être délicate. Je sais que la bibliothèque a des classes intégrées pour cela, mais je ne les ai jamais aimées. Elles prennent plusieurs canaux d'entrée et les démultiplexent en un seul canal. Vous pouvez les voir en action dans test.cpp , fonctions SecretRecoverFile (autour de la ligne 650) et InformationRecoverFile (autour de la ligne 700).


Existe-t-il un moyen de transmettre à l'entité de vérification deux sources arbitraires dont l'une représente la signature et l'autre le contenu signé (qui peut être un fichier ou une chaîne) ?

Voici comment je m'y prendrais pour faire ce que vous voulez faire. L'exemple ci-dessous utilise deux sources et partage une chaîne de filtres. J'ai réduit la complexité en hachant deux chaînes de caractères à l'aide d'un fichier HashFilter . Votre exemple utilise le message, la signature, les paires de clés et les clés de sécurité. SignatureVerificationFilter mais c'est plus complexe que nécessaire pour vous montrer comment faire.

L'exemple se déroule en quatre parties :

  • Partie 0 - configurer les données. Deux chaînes ASCII de 16K sont créées. Une chaîne est également écrite dans un fichier.
  • Partie 1 - imprimer les données. Hash(s1) , Hash(s2) y Hash(s1+s2) sont imprimés.
  • Partie 2 - utiliser deux sources de chaînes. Hash(s1+s2) est créé à l'aide de deux StringSources
  • Partie 3 - utiliser une source de type chaîne et une source de type fichier. Hash(s1+s2) est créé en utilisant un StringSource et un FileSource

Pour énoncer l'évidence, l'exemple simplifié calcule Hash(s1+s2) . Dans votre contexte, l'opération est Verify(key, s1+s2) , donde key est la clé publique, s1 est la signature et s2 est le contenu du fichier.

Partie 0 - Les données sont configurées ci-dessous. C'est assez ennuyeux. Notez s3 est une concaténation de s1 y s2 .

std::string s1, s2, s3;
const size_t size = 1024*16+1;

random_string(s1, size);
random_string(s2, size);

s3 = s1 + s2;

Partie 1 - Les données sont imprimées ci-dessous. Les hachages de s1 , s2 y s3 sont imprimés. s3 est le plus important. s3 est ce à quoi nous devons arriver en utilisant deux sources distinctes.

std::string r;
StringSource ss1(s1, true, new HashFilter(hash, new StringSink(r)));

std::cout << "s1: ";
hex.Put((const byte*)r.data(), r.size());
std::cout << std::endl;

r.clear();
StringSource ss2(s2, true, new HashFilter(hash, new StringSink(r)));

std::cout << "s2: ";
hex.Put((const byte*)r.data(), r.size());
std::cout << std::endl;

r.clear();
StringSource ss3(s3, true, new HashFilter(hash, new StringSink(r)));

std::cout << "s3: ";
hex.Put((const byte*)r.data(), r.size());
std::cout << std::endl;

La sortie ressemble à ça :

$ ./test.exe
s1: 45503354F9BC56C9B5B61276375A4C60F83A2F01
s2: 6A3AD5B683DE7CA57F07E8099268A8BC80FA200B
s3: BFC1882CEB24697A2B34D7CF8B95604B7109F28D
...

Partie 2 - C'est là que les choses deviennent intéressantes. Nous utilisons deux StringSource à traiter s1 y s2 individuellement.

StringSource ss4(s1, false);
StringSource ss5(s2, false);

HashFilter hf1(hash, new StringSink(r));

ss4.Attach(new Redirector(hf1));
ss4.Pump(LWORD_MAX);
ss4.Detach();

ss5.Attach(new Redirector(hf1));
ss5.Pump(LWORD_MAX);
ss5.Detach();

hf1.MessageEnd();

std::cout << "s1 + s2: ";
hex.Put((const byte*)r.data(), r.size());
std::cout << std::endl;

Il produit le résultat suivant :

$ ./test.exe
s1: 45503354F9BC56C9B5B61276375A4C60F83A2F01
s2: 6A3AD5B683DE7CA57F07E8099268A8BC80FA200B
s3: BFC1882CEB24697A2B34D7CF8B95604B7109F28D
s1 + s2: BFC1882CEB24697A2B34D7CF8B95604B7109F28D
...

Il y a plusieurs choses qui se passent dans le code ci-dessus. Premièrement, nous attachons et détachons dynamiquement la chaîne de filtres de hachage aux sources ss4 y ss5 .

Ensuite, une fois que le filtre est fixé, nous utilisons Pump(LWORD_MAX) pour pomper toutes les données de la source dans la chaîne de filtrage. Nous n'utilisons pas PumpAll() porque PumpAll() signale la fin du message en cours et génère un message MessageEnd() . Nous traitons un message en plusieurs parties ; nous ne traitons pas plusieurs messages. Nous ne voulons donc qu'un seul MessageEnd() lorsque nous déterminons.

Troisièmement, une fois que nous avons terminé avec la source, nous appelons Detach así que StringSource destructeurs Ne le fais pas. provoquer un faux MessageEnd() pour entrer dans la chaîne de filtrage. Encore une fois, nous traitons un message en plusieurs parties ; nous ne traitons pas de multiples messages. Nous voulons donc qu'un seul MessageEnd() lorsque nous déterminons.

Quatrièmement, lorsque nous avons fini d'envoyer nos données dans le filtre, nous appelons hf.MessageEnd() pour indiquer au filtre de traiter toutes les données en attente ou en mémoire tampon. C'est à ce moment-là que nous voulons que le MessageEnd() l'appel, et pas avant.

Cinquièmement, nous appelons Detach() lorsqu'il est fait plutôt que Attach() . Detach() supprime la chaîne de filtres existante et évite les fuites de mémoire. Attach() attache une nouvelle chaîne mais ne no supprimer le filtre ou la chaîne existante. Puisque nous utilisons un Redirector notre HashFilter survit. Le site HashFilter est éventuellement nettoyé comme une variable de pile automatique.

Soit dit en passant, si ss4.PumpAll() y ss5.PumpAll() étaient utilisés (ou permettaient aux destructeurs d'envoyer des MessageEnd() dans la chaîne de filtrage), on obtiendrait alors une concaténation de Hash(s1) y Hash(s2) parce que cela ressemblerait à deux messages différents pour le filtre au lieu d'un seul message sur deux parties. Le code ci-dessous est erroné :

StringSource ss4(s1, false);
StringSource ss5(s2, false);

HashFilter hf1(hash, new StringSink(r));

ss4.Attach(new Redirector(hf1));
// ss4.Pump(LWORD_MAX);
ss4.PumpAll();  // MessageEnd
ss4.Detach();

ss5.Attach(new Redirector(hf1));
// ss5.Pump(LWORD_MAX);
ss5.PumpAll();  // MessageEnd
ss5.Detach();

// Third MessageEnd
hf1.MessageEnd();

Le code incorrect ci-dessus produit Hash(s1) || Hash(s2) || Hash(<empty string>) :

$ ./test.exe
s1: 45503354F9BC56C9B5B61276375A4C60F83A2F01
s2: 6A3AD5B683DE7CA57F07E8099268A8BC80FA200B
s3: BFC1882CEB24697A2B34D7CF8B95604B7109F28D
s1 + s2: 45503354F9BC56C9B5B61276375A4C60F83A2F016A3AD5B683DE7CA57F07E8099268A8BC80FA200BDA39A3EE5E6B4B0D3255BFEF95601890AFD80709

Partie 3 - C'est votre cas d'utilisation. Nous utilisons un StringSource y FileSource à traiter s1 y s2 individuellement. N'oubliez pas que la chaîne s2 a été écrit dans un fichier nommé test.dat .

StringSource ss6(s1, false);
FileSource fs1("test.dat", false);

HashFilter hf2(hash, new StringSink(r));

ss6.Attach(new Redirector(hf2));
ss6.Pump(LWORD_MAX);
ss6.Detach();

fs1.Attach(new Redirector(hf2));
fs1.Pump(LWORD_MAX);
fs1.Detach();

hf2.MessageEnd();

std::cout << "s1 + s2 (file): ";
hex.Put((const byte*)r.data(), r.size());
std::cout << std::endl;

Voici à quoi ressemble l'exécution de l'exemple complet :

$ ./test.exe
s1: 45503354F9BC56C9B5B61276375A4C60F83A2F01
s2: 6A3AD5B683DE7CA57F07E8099268A8BC80FA200B
s3: BFC1882CEB24697A2B34D7CF8B95604B7109F28D
s1 + s2: BFC1882CEB24697A2B34D7CF8B95604B7109F28D
s1 + s2 (file): BFC1882CEB24697A2B34D7CF8B95604B7109F28D

Avis s3 = s1 + s2 = s1 + s2 (file) .


$ cat test.cxx

#include "cryptlib.h"
#include "filters.h"
#include "files.h"
#include "sha.h"
#include "hex.h"

#include <string>
#include <iostream>

void random_string(std::string& str, size_t len)
{
    const char alphanum[] =
        "0123456789"
        "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
        "abcdefghijklmnopqrstuvwxyz";
    const size_t size = sizeof(alphanum) - 1;

    str.reserve(len);
    for (size_t i = 0; i < len; ++i)
        str.push_back(alphanum[rand() % size]);
}

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

    ////////////////////////// Part 0 //////////////////////////

    // Deterministic
    std::srand(0);

    std::string s1, s2, s3, r;
    const size_t size = 1024*16+1;

    random_string(s1, size);
    random_string(s2, size);

    // Concatenate for verification
    s3 = s1 + s2;

    // Write s2 to file
    StringSource(s2, true, new FileSink("test.dat"));

    // Hashing, resets after use
    SHA1 hash;

    // Printing hex encoded string to std::cout
    HexEncoder hex(new FileSink(std::cout));

    ////////////////////////// Part 1 //////////////////////////

    r.clear();
    StringSource ss1(s1, true, new HashFilter(hash, new StringSink(r)));

    std::cout << "s1: ";
    hex.Put((const byte*)r.data(), r.size());
    std::cout << std::endl;

    r.clear();
    StringSource ss2(s2, true, new HashFilter(hash, new StringSink(r)));

    std::cout << "s2: ";
    hex.Put((const byte*)r.data(), r.size());
    std::cout << std::endl;

    r.clear();
    StringSource ss3(s3, true, new HashFilter(hash, new StringSink(r)));

    std::cout << "s3: ";
    hex.Put((const byte*)r.data(), r.size());
    std::cout << std::endl;

    ////////////////////////// Part 2 //////////////////////////

    r.clear();
    StringSource ss4(s1, false);
    StringSource ss5(s2, false);

    HashFilter hf1(hash, new StringSink(r));

    ss4.Attach(new Redirector(hf1));
    ss4.Pump(LWORD_MAX);
    ss4.Detach();

    ss5.Attach(new Redirector(hf1));
    ss5.Pump(LWORD_MAX);
    ss5.Detach();

    hf1.MessageEnd();

    std::cout << "s1 + s2: ";
    hex.Put((const byte*)r.data(), r.size());
    std::cout << std::endl;

    ////////////////////////// Part 3 //////////////////////////

    r.clear();
    StringSource ss6(s1, false);
    FileSource fs1("test.dat", false);

    HashFilter hf2(hash, new StringSink(r));

    ss6.Attach(new Redirector(hf2));
    ss6.Pump(LWORD_MAX);
    ss6.Detach();

    fs1.Attach(new Redirector(hf2));
    fs1.Pump(LWORD_MAX);
    fs1.Detach();

    hf2.MessageEnd();

    std::cout << "s1 + s2 (file): ";
    hex.Put((const byte*)r.data(), r.size());
    std::cout << std::endl;

    return 0;
}

Et :

$ g++ test.cxx ./libcryptopp.a -o test.exe
$ ./test.exe
s1: 45503354F9BC56C9B5B61276375A4C60F83A2F01
s2: 6A3AD5B683DE7CA57F07E8099268A8BC80FA200B
s3: BFC1882CEB24697A2B34D7CF8B95604B7109F28D
s1 + s2: BFC1882CEB24697A2B34D7CF8B95604B7109F28D
s1 + s2 (file): BFC1882CEB24697A2B34D7CF8B95604B7109F28D

Voici un cours qui pourrait vous soulager. Il rassemble les concepts ci-dessus dans un MultipleSources classe. MultipleSources n'est qu'une mise en œuvre partielle de la Source mais il devrait avoir toutes les pièces dont vous avez besoin.

class MultipleSources
{
public:
    MultipleSources(std::vector<Source*>& source, Filter& filter)
    : m_s(source), m_f(filter)
    {
    }

    void Pump(lword pumpMax, bool messageEnd)
    {
        for (size_t i=0; pumpMax && i<m_s.size(); ++i)
        {
            lword n = pumpMax;
            m_s[i]->Attach(new Redirector(m_f));            
            m_s[i]->Pump2(n);
            m_s[i]->Detach();
            pumpMax -= n;
        }

        if (messageEnd)
            m_f.MessageEnd();
    }

    void PumpAll()
    {
        for (size_t i=0; i<m_s.size(); ++i)
        {
            m_s[i]->Attach(new Redirector(m_f));
            m_s[i]->Pump(LWORD_MAX);
            m_s[i]->Detach();
        }

        m_f.MessageEnd();
    }

private:
    std::vector<Source*>& m_s;
    Filter &m_f;
};

Vous l'appelleriez comme ça :

StringSource ss(s1, false);
FileSource fs("test.dat", false);
HashFilter hf(hash, new StringSink(r));

std::vector<Source*> srcs;
srcs.push_back(&ss);
srcs.push_back(&fs);

MultipleSources ms(srcs, hf);
ms.Pump(LWORD_MAX, false);

hf.MessageEnd();

Ou vous pouvez utiliser PumpAll et obtenir le même résultat, mais vous n'appelez pas hf.MessageEnd(); dans ce cas, car PumpAll signale la fin du message.

MultipleSources ms(srcs, hf);
ms.PumpAll();

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X