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();