apt  2.2.4
About: Apt (Advanced Package Tool) is a management system for software packages (Debian/Ubuntu). Release series 2.2.
  Fossies Dox: apt-2.2.4.tar.gz  ("unofficial" and yet experimental doxygen-generated source code documentation)  

hashes.cc
Go to the documentation of this file.
1 // -*- mode: cpp; mode: fold -*-
2 // Description /*{{{*/
3 /* ######################################################################
4 
5  Hashes - Simple wrapper around the hash functions
6 
7  This is just used to make building the methods simpler, this is the
8  only interface required..
9 
10  ##################################################################### */
11  /*}}}*/
12 // Include Files /*{{{*/
13 #include <config.h>
14 
15 #include <apt-pkg/configuration.h>
16 #include <apt-pkg/fileutl.h>
17 #include <apt-pkg/hashes.h>
18 #include <apt-pkg/macros.h>
19 #include <apt-pkg/strutl.h>
20 
21 #include <algorithm>
22 #include <iostream>
23 #include <string>
24 #include <assert.h>
25 #include <stddef.h>
26 #include <stdlib.h>
27 #include <unistd.h>
28 
29 #include <gcrypt.h>
30  /*}}}*/
31 
32 static const constexpr struct HashAlgo
33 {
34  const char *name;
35  int gcryAlgo;
37 } Algorithms[] = {
38  {"MD5Sum", GCRY_MD_MD5, Hashes::MD5SUM},
39  {"SHA1", GCRY_MD_SHA1, Hashes::SHA1SUM},
40  {"SHA256", GCRY_MD_SHA256, Hashes::SHA256SUM},
41  {"SHA512", GCRY_MD_SHA512, Hashes::SHA512SUM},
42 };
43 
44 const char * HashString::_SupportedHashes[] =
45 {
46  "SHA512", "SHA256", "SHA1", "MD5Sum", "Checksum-FileSize", NULL
47 };
48 
50 {
51 }
52 
53 HashString::HashString(std::string Type, std::string Hash) : Type(Type), Hash(Hash)
54 {
55 }
56 
57 HashString::HashString(std::string StringedHash) /*{{{*/
58 {
59  if (StringedHash.find(":") == std::string::npos)
60  {
61  // legacy: md5sum without "MD5Sum:" prefix
62  if (StringedHash.size() == 32)
63  {
64  Type = "MD5Sum";
65  Hash = StringedHash;
66  }
67  if(_config->FindB("Debug::Hashes",false) == true)
68  std::clog << "HashString(string): invalid StringedHash " << StringedHash << std::endl;
69  return;
70  }
71  std::string::size_type pos = StringedHash.find(":");
72  Type = StringedHash.substr(0,pos);
73  Hash = StringedHash.substr(pos+1, StringedHash.size() - pos);
74 
75  if(_config->FindB("Debug::Hashes",false) == true)
76  std::clog << "HashString(string): " << Type << " : " << Hash << std::endl;
77 }
78  /*}}}*/
79 bool HashString::VerifyFile(std::string filename) const /*{{{*/
80 {
81  std::string fileHash = GetHashForFile(filename);
82 
83  if(_config->FindB("Debug::Hashes",false) == true)
84  std::clog << "HashString::VerifyFile: got: " << fileHash << " expected: " << toStr() << std::endl;
85 
86  return (fileHash == Hash);
87 }
88  /*}}}*/
89 bool HashString::FromFile(std::string filename) /*{{{*/
90 {
91  // pick the strongest hash
92  if (Type == "")
94 
95  Hash = GetHashForFile(filename);
96  return true;
97 }
98  /*}}}*/
99 std::string HashString::GetHashForFile(std::string filename) const /*{{{*/
100 {
101  std::string fileHash;
102 
103  FileFd Fd(filename, FileFd::ReadOnly);
104  if(strcasecmp(Type.c_str(), "MD5Sum") == 0)
105  {
106  Hashes MD5(Hashes::MD5SUM);
107  MD5.AddFD(Fd);
108  fileHash = MD5.GetHashString(Hashes::MD5SUM).Hash;
109  }
110  else if (strcasecmp(Type.c_str(), "SHA1") == 0)
111  {
112  Hashes SHA1(Hashes::SHA1SUM);
113  SHA1.AddFD(Fd);
114  fileHash = SHA1.GetHashString(Hashes::SHA1SUM).Hash;
115  }
116  else if (strcasecmp(Type.c_str(), "SHA256") == 0)
117  {
118  Hashes SHA256(Hashes::SHA256SUM);
119  SHA256.AddFD(Fd);
120  fileHash = SHA256.GetHashString(Hashes::SHA256SUM).Hash;
121  }
122  else if (strcasecmp(Type.c_str(), "SHA512") == 0)
123  {
124  Hashes SHA512(Hashes::SHA512SUM);
125  SHA512.AddFD(Fd);
126  fileHash = SHA512.GetHashString(Hashes::SHA512SUM).Hash;
127  }
128  else if (strcasecmp(Type.c_str(), "Checksum-FileSize") == 0)
129  strprintf(fileHash, "%llu", Fd.FileSize());
130  Fd.Close();
131 
132  return fileHash;
133 }
134  /*}}}*/
135 const char** HashString::SupportedHashes() /*{{{*/
136 {
137  return _SupportedHashes;
138 }
139  /*}}}*/
140 APT_PURE bool HashString::empty() const /*{{{*/
141 {
142  return (Type.empty() || Hash.empty());
143 }
144  /*}}}*/
145 
146 APT_PURE static bool IsConfigured(const char *name, const char *what)
147 {
148  std::string option;
149  strprintf(option, "APT::Hashes::%s::%s", name, what);
150  return _config->FindB(option, false);
151 }
152 
153 APT_PURE bool HashString::usable() const /*{{{*/
154 {
155  return (
156  (Type != "Checksum-FileSize") &&
157  (Type != "MD5Sum") &&
158  (Type != "SHA1") &&
159  !IsConfigured(Type.c_str(), "Untrusted")
160  );
161 }
162  /*}}}*/
163 std::string HashString::toStr() const /*{{{*/
164 {
165  return Type + ":" + Hash;
166 }
167  /*}}}*/
168 APT_PURE bool HashString::operator==(HashString const &other) const /*{{{*/
169 {
170  return (strcasecmp(Type.c_str(), other.Type.c_str()) == 0 && Hash == other.Hash);
171 }
173 {
174  return !(*this == other);
175 }
176  /*}}}*/
177 
178 bool HashStringList::usable() const /*{{{*/
179 {
180  if (empty() == true)
181  return false;
182  std::string const forcedType = _config->Find("Acquire::ForceHash", "");
183  if (forcedType.empty() == true)
184  {
185  // See if there is at least one usable hash
186  return std::any_of(list.begin(), list.end(), [](auto const &hs) { return hs.usable(); });
187  }
188  return find(forcedType) != NULL;
189 }
190  /*}}}*/
191 HashString const * HashStringList::find(char const * const type) const /*{{{*/
192 {
193  if (type == NULL || type[0] == '\0')
194  {
195  std::string const forcedType = _config->Find("Acquire::ForceHash", "");
196  if (forcedType.empty() == false)
197  return find(forcedType.c_str());
198  for (char const * const * t = HashString::SupportedHashes(); *t != NULL; ++t)
199  for (std::vector<HashString>::const_iterator hs = list.begin(); hs != list.end(); ++hs)
200  if (strcasecmp(hs->HashType().c_str(), *t) == 0)
201  return &*hs;
202  return NULL;
203  }
204  for (std::vector<HashString>::const_iterator hs = list.begin(); hs != list.end(); ++hs)
205  if (strcasecmp(hs->HashType().c_str(), type) == 0)
206  return &*hs;
207  return NULL;
208 }
209  /*}}}*/
210 unsigned long long HashStringList::FileSize() const /*{{{*/
211 {
212  HashString const * const hsf = find("Checksum-FileSize");
213  if (hsf == NULL)
214  return 0;
215  std::string const hv = hsf->HashValue();
216  return strtoull(hv.c_str(), NULL, 10);
217 }
218  /*}}}*/
219 bool HashStringList::FileSize(unsigned long long const Size) /*{{{*/
220 {
221  return push_back(HashString("Checksum-FileSize", std::to_string(Size)));
222 }
223  /*}}}*/
224 bool HashStringList::supported(char const * const type) /*{{{*/
225 {
226  for (char const * const * t = HashString::SupportedHashes(); *t != NULL; ++t)
227  if (strcasecmp(*t, type) == 0)
228  return true;
229  return false;
230 }
231  /*}}}*/
232 bool HashStringList::push_back(const HashString &hashString) /*{{{*/
233 {
234  if (hashString.HashType().empty() == true ||
235  hashString.HashValue().empty() == true ||
236  supported(hashString.HashType().c_str()) == false)
237  return false;
238 
239  // ensure that each type is added only once
240  HashString const * const hs = find(hashString.HashType().c_str());
241  if (hs != NULL)
242  return *hs == hashString;
243 
244  list.push_back(hashString);
245  return true;
246 }
247  /*}}}*/
248 bool HashStringList::VerifyFile(std::string filename) const /*{{{*/
249 {
250  if (usable() == false)
251  return false;
252 
253  Hashes hashes(*this);
254  FileFd file(filename, FileFd::ReadOnly);
255  HashString const * const hsf = find("Checksum-FileSize");
256  if (hsf != NULL)
257  {
258  std::string fileSize;
259  strprintf(fileSize, "%llu", file.FileSize());
260  if (hsf->HashValue() != fileSize)
261  return false;
262  }
263  hashes.AddFD(file);
264  HashStringList const hsl = hashes.GetHashStringList();
265  return hsl == *this;
266 }
267  /*}}}*/
268 bool HashStringList::operator==(HashStringList const &other) const /*{{{*/
269 {
270  std::string const forcedType = _config->Find("Acquire::ForceHash", "");
271  if (forcedType.empty() == false)
272  {
273  HashString const * const hs = find(forcedType);
274  HashString const * const ohs = other.find(forcedType);
275  if (hs == NULL || ohs == NULL)
276  return false;
277  return *hs == *ohs;
278  }
279  short matches = 0;
280  for (const_iterator hs = begin(); hs != end(); ++hs)
281  {
282  HashString const * const ohs = other.find(hs->HashType());
283  if (ohs == NULL)
284  continue;
285  if (*hs != *ohs)
286  return false;
287  ++matches;
288  }
289  if (matches == 0)
290  return false;
291  return true;
292 }
294 {
295  return !(*this == other);
296 }
297  /*}}}*/
298 
299 // PrivateHashes /*{{{*/
301 public:
302  unsigned long long FileSize;
303  gcry_md_hd_t hd;
304 
305  void maybeInit()
306  {
307 
308  // Yikes, we got to initialize libgcrypt, or we get warnings. But we
309  // abstract away libgcrypt in Hashes from our users - they are not
310  // supposed to know what the hashing backend is, so we can't force
311  // them to init themselves as libgcrypt folks want us to. So this
312  // only leaves us with this option...
313  if (!gcry_control(GCRYCTL_INITIALIZATION_FINISHED_P))
314  {
315  if (!gcry_check_version(nullptr))
316  {
317  fprintf(stderr, "libgcrypt is too old (need %s, have %s)\n",
318  "nullptr", gcry_check_version(NULL));
319  exit(2);
320  }
321 
322  gcry_control(GCRYCTL_INITIALIZATION_FINISHED, 0);
323  }
324  }
325 
326  explicit PrivateHashes(unsigned int const CalcHashes) : FileSize(0)
327  {
328  maybeInit();
329  gcry_md_open(&hd, 0, 0);
330  for (auto & Algo : Algorithms)
331  {
332  if ((CalcHashes & Algo.ourAlgo) == Algo.ourAlgo)
333  gcry_md_enable(hd, Algo.gcryAlgo);
334  }
335  }
336 
337  explicit PrivateHashes(HashStringList const &Hashes) : FileSize(0) {
338  maybeInit();
339  gcry_md_open(&hd, 0, 0);
340  for (auto & Algo : Algorithms)
341  {
342  if (not Hashes.usable() || Hashes.find(Algo.name) != NULL)
343  gcry_md_enable(hd, Algo.gcryAlgo);
344  }
345  }
347  {
348  gcry_md_close(hd);
349  }
350 };
351  /*}}}*/
352 // Hashes::Add* - Add the contents of data or FD /*{{{*/
353 bool Hashes::Add(const unsigned char * const Data, unsigned long long const Size)
354 {
355  if (Size != 0)
356  {
357  gcry_md_write(d->hd, Data, Size);
358  d->FileSize += Size;
359  }
360  return true;
361 }
362 bool Hashes::AddFD(int const Fd,unsigned long long Size)
363 {
364  unsigned char Buf[APT_BUFFER_SIZE];
365  bool const ToEOF = (Size == UntilEOF);
366  while (Size != 0 || ToEOF)
367  {
368  decltype(Size) n = sizeof(Buf);
369  if (!ToEOF) n = std::min(Size, n);
370  ssize_t const Res = read(Fd,Buf,n);
371  if (Res < 0 || (!ToEOF && Res != (ssize_t) n)) // error, or short read
372  return false;
373  if (ToEOF && Res == 0) // EOF
374  break;
375  Size -= Res;
376  if (Add(Buf, Res) == false)
377  return false;
378  }
379  return true;
380 }
381 bool Hashes::AddFD(FileFd &Fd,unsigned long long Size)
382 {
383  unsigned char Buf[APT_BUFFER_SIZE];
384  bool const ToEOF = (Size == 0);
385  while (Size != 0 || ToEOF)
386  {
387  decltype(Size) n = sizeof(Buf);
388  if (!ToEOF) n = std::min(Size, n);
389  decltype(Size) a = 0;
390  if (Fd.Read(Buf, n, &a) == false) // error
391  return false;
392  if (ToEOF == false)
393  {
394  if (a != n) // short read
395  return false;
396  }
397  else if (a == 0) // EOF
398  break;
399  Size -= a;
400  if (Add(Buf, a) == false)
401  return false;
402  }
403  return true;
404 }
405  /*}}}*/
406 
407 static APT_PURE std::string HexDigest(gcry_md_hd_t hd, int algo)
408 {
409  char Conv[16] =
410  {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b',
411  'c', 'd', 'e', 'f'};
412 
413  auto Size = gcry_md_get_algo_dlen(algo);
414  assert(Size <= 512/8);
415  char Result[((Size)*2) + 1];
416  Result[(Size)*2] = 0;
417 
418  auto Sum = gcry_md_read(hd, algo);
419 
420  // Convert each char into two letters
421  size_t J = 0;
422  size_t I = 0;
423  for (; I != (Size)*2; J++, I += 2)
424  {
425  Result[I] = Conv[Sum[J] >> 4];
426  Result[I + 1] = Conv[Sum[J] & 0xF];
427  }
428  return std::string(Result);
429 };
430 
432 {
433  HashStringList hashes;
434  for (auto & Algo : Algorithms)
435  if (gcry_md_is_enabled(d->hd, Algo.gcryAlgo))
436  hashes.push_back(HashString(Algo.name, HexDigest(d->hd, Algo.gcryAlgo)));
437  hashes.FileSize(d->FileSize);
438 
439  return hashes;
440 }
441 
443 {
444  for (auto & Algo : Algorithms)
445  if (hash == Algo.ourAlgo)
446  return HashString(Algo.name, HexDigest(d->hd, Algo.gcryAlgo));
447 
448  abort();
449 }
450 Hashes::Hashes() : d(new PrivateHashes(~0)) { }
451 Hashes::Hashes(unsigned int const Hashes) : d(new PrivateHashes(Hashes)) {}
453 Hashes::~Hashes() { delete d; }
strprintf(m, msg, repo.c_str())
static bool std::string const metaIndex const *const pkgAcqMetaClearSig *const pkgAcquire::Item *const I
std::string Find(const char *Name, const char *Default=0) const
bool FindB(const char *Name, bool const &Default=false) const
Definition: fileutl.h:39
@ ReadOnly
Definition: fileutl.h:59
unsigned long long FileSize()
Definition: fileutl.cc:2943
bool Read(void *To, unsigned long long Size, bool AllowEof)
Definition: fileutl.h:89
bool Close()
Definition: fileutl.cc:2977
bool usable() const
Definition: hashes.cc:178
bool VerifyFile(std::string filename) const
Definition: hashes.cc:248
bool operator!=(HashStringList const &other) const
Definition: hashes.cc:293
std::vector< HashString >::const_iterator const_iterator
Definition: hashes.h:130
std::vector< HashString > list
Definition: hashes.h:165
bool empty() const
Definition: hashes.h:119
HashString const * find(char const *const type) const
Definition: hashes.cc:191
bool push_back(const HashString &hashString)
Definition: hashes.cc:232
bool operator==(HashStringList const &other) const
Definition: hashes.cc:268
static APT_PURE bool supported(char const *const type)
Definition: hashes.cc:224
unsigned long long FileSize() const
Definition: hashes.cc:210
static const char * _SupportedHashes[10]
Definition: hashes.h:32
bool VerifyFile(std::string filename) const
Definition: hashes.cc:79
std::string GetHashForFile(std::string filename) const
Definition: hashes.cc:99
std::string Hash
Definition: hashes.h:31
HashString()
Definition: hashes.cc:49
bool operator!=(HashString const &other) const
Definition: hashes.cc:172
bool usable() const
Definition: hashes.cc:153
std::string toStr() const
Definition: hashes.cc:163
std::string Type
Definition: hashes.h:30
static APT_PURE const char ** SupportedHashes()
Definition: hashes.cc:135
bool FromFile(std::string filename)
Definition: hashes.cc:89
bool operator==(HashString const &other) const
Definition: hashes.cc:168
std::string HashValue() const
Definition: hashes.h:44
std::string HashType() const
Definition: hashes.h:43
bool empty() const
Definition: hashes.cc:140
Definition: hashes.h:170
SupportedHashes
Definition: hashes.h:185
@ MD5SUM
Definition: hashes.h:185
@ SHA256SUM
Definition: hashes.h:185
@ SHA512SUM
Definition: hashes.h:186
@ SHA1SUM
Definition: hashes.h:185
virtual ~Hashes()
Definition: hashes.cc:453
HashString GetHashString(SupportedHashes hash)
Definition: hashes.cc:442
HashStringList GetHashStringList()
Definition: hashes.cc:431
static const int UntilEOF
Definition: hashes.h:173
bool Add(const unsigned char *const Data, unsigned long long const Size) APT_NONNULL(2)
Definition: hashes.cc:353
Hashes()
Definition: hashes.cc:450
PrivateHashes *const d
Definition: hashes.h:171
bool AddFD(int const Fd, unsigned long long Size=0)
Definition: hashes.cc:362
PrivateHashes(unsigned int const CalcHashes)
Definition: hashes.cc:326
void maybeInit()
Definition: hashes.cc:305
unsigned long long FileSize
Definition: hashes.cc:302
gcry_md_hd_t hd
Definition: hashes.cc:303
PrivateHashes(HashStringList const &Hashes)
Definition: hashes.cc:337
~PrivateHashes()
Definition: hashes.cc:346
Configuration * _config
static APT_PURE std::string HexDigest(gcry_md_hd_t hd, int algo)
Definition: hashes.cc:407
static constexpr const struct HashAlgo Algorithms[]
static APT_PURE bool IsConfigured(const char *name, const char *what)
Definition: hashes.cc:146
#define APT_PURE
Definition: macros.h:56
static constexpr unsigned long long APT_BUFFER_SIZE
Definition: macros.h:125
Hashes::SupportedHashes ourAlgo
Definition: hashes.cc:36
const char * name
Definition: hashes.cc:34
int gcryAlgo
Definition: hashes.cc:35