digest.cc
Go to the documentation of this file.
1 /* Copyright (c) 2014, Google Inc.
2  *
3  * Permission to use, copy, modify, and/or distribute this software for any
4  * purpose with or without fee is hereby granted, provided that the above
5  * copyright notice and this permission notice appear in all copies.
6  *
7  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
8  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
9  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
10  * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
11  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
12  * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
13  * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */
14 
15 #include <openssl/base.h>
16 
17 #include <memory>
18 #include <string>
19 #include <vector>
20 
21 #include <errno.h>
22 #include <fcntl.h>
23 #include <limits.h>
24 #include <stdio.h>
25 #include <sys/stat.h>
26 #include <sys/types.h>
27 
28 #if !defined(OPENSSL_WINDOWS)
29 #include <string.h>
30 #include <unistd.h>
31 #if !defined(O_BINARY)
32 #define O_BINARY 0
33 #endif
34 #else
36 #include <windows.h>
38 #include <io.h>
39 #if !defined(PATH_MAX)
40 #define PATH_MAX MAX_PATH
41 #endif
42 #endif
43 
44 #include <openssl/digest.h>
45 
46 #include "internal.h"
47 
48 
49 // Source is an awkward expression of a union type in C++: Stdin | File filename.
50 struct Source {
51  enum Type {
53  };
54 
56  explicit Source(Type) : is_stdin_(true) {}
57  explicit Source(const std::string &name)
59 
60  bool is_stdin() const { return is_stdin_; }
61  const std::string &filename() const { return filename_; }
62 
63  private:
64  bool is_stdin_;
66 };
67 
68 static const char kStdinName[] = "standard input";
69 
70 // OpenFile opens the regular file named |filename| and returns a file
71 // descriptor to it.
73  ScopedFD fd = OpenFD(filename.c_str(), O_RDONLY | O_BINARY);
74  if (!fd) {
75  fprintf(stderr, "Failed to open input file '%s': %s\n", filename.c_str(),
76  strerror(errno));
77  return ScopedFD();
78  }
79 
80 #if !defined(OPENSSL_WINDOWS)
81  struct stat st;
82  if (fstat(fd.get(), &st)) {
83  fprintf(stderr, "Failed to stat input file '%s': %s\n", filename.c_str(),
84  strerror(errno));
85  return ScopedFD();
86  }
87 
88  if (!S_ISREG(st.st_mode)) {
89  fprintf(stderr, "%s: not a regular file\n", filename.c_str());
90  return ScopedFD();
91  }
92 #endif
93 
94  return fd;
95 }
96 
97 // SumFile hashes the contents of |source| with |md| and sets |*out_hex| to the
98 // hex-encoded result.
99 //
100 // It returns true on success or prints an error to stderr and returns false on
101 // error.
102 static bool SumFile(std::string *out_hex, const EVP_MD *md,
103  const Source &source) {
104  ScopedFD scoped_fd;
105  int fd;
106 
107  if (source.is_stdin()) {
108  fd = 0;
109  } else {
110  scoped_fd = OpenFile(source.filename());
111  if (!scoped_fd) {
112  return false;
113  }
114  fd = scoped_fd.get();
115  }
116 
117  static const size_t kBufSize = 8192;
118  std::unique_ptr<uint8_t[]> buf(new uint8_t[kBufSize]);
119 
120  bssl::ScopedEVP_MD_CTX ctx;
121  if (!EVP_DigestInit_ex(ctx.get(), md, NULL)) {
122  fprintf(stderr, "Failed to initialize EVP_MD_CTX.\n");
123  return false;
124  }
125 
126  for (;;) {
127  size_t n;
128  if (!ReadFromFD(fd, &n, buf.get(), kBufSize)) {
129  fprintf(stderr, "Failed to read from %s: %s\n",
130  source.is_stdin() ? kStdinName : source.filename().c_str(),
131  strerror(errno));
132  return false;
133  }
134 
135  if (n == 0) {
136  break;
137  }
138 
139  if (!EVP_DigestUpdate(ctx.get(), buf.get(), n)) {
140  fprintf(stderr, "Failed to update hash.\n");
141  return false;
142  }
143  }
144 
145  uint8_t digest[EVP_MAX_MD_SIZE];
146  unsigned digest_len;
147  if (!EVP_DigestFinal_ex(ctx.get(), digest, &digest_len)) {
148  fprintf(stderr, "Failed to finish hash.\n");
149  return false;
150  }
151 
152  char hex_digest[EVP_MAX_MD_SIZE * 2];
153  static const char kHextable[] = "0123456789abcdef";
154  for (unsigned i = 0; i < digest_len; i++) {
155  const uint8_t b = digest[i];
156  hex_digest[i * 2] = kHextable[b >> 4];
157  hex_digest[i * 2 + 1] = kHextable[b & 0xf];
158  }
159  *out_hex = std::string(hex_digest, digest_len * 2);
160 
161  return true;
162 }
163 
164 // PrintFileSum hashes |source| with |md| and prints a line to stdout in the
165 // format of the coreutils *sum utilities. It returns true on success or prints
166 // an error to stderr and returns false on error.
167 static bool PrintFileSum(const EVP_MD *md, const Source &source) {
168  std::string hex_digest;
169  if (!SumFile(&hex_digest, md, source)) {
170  return false;
171  }
172 
173  // TODO: When given "--binary" or "-b", we should print " *" instead of " "
174  // between the digest and the filename.
175  //
176  // MSYS and Cygwin md5sum default to binary mode by default, whereas other
177  // platforms' tools default to text mode by default. We default to text mode
178  // by default and consider text mode equivalent to binary mode (i.e. we
179  // always use Unix semantics, even on Windows), which means that our default
180  // output will differ from the MSYS and Cygwin tools' default output.
181  printf("%s %s\n", hex_digest.c_str(),
182  source.is_stdin() ? "-" : source.filename().c_str());
183  return true;
184 }
185 
186 // CheckModeArguments contains arguments for the check mode. See the
187 // sha256sum(1) man page for details.
189  bool quiet = false;
190  bool status = false;
191  bool warn = false;
192  bool strict = false;
193 };
194 
195 // Check reads lines from |source| where each line is in the format of the
196 // coreutils *sum utilities. It attempts to verify each hash by reading the
197 // file named in the line.
198 //
199 // It returns true if all files were verified and, if |args.strict|, no input
200 // lines had formatting errors. Otherwise it prints errors to stderr and
201 // returns false.
202 static bool Check(const CheckModeArguments &args, const EVP_MD *md,
203  const Source &source) {
204  FILE *file;
205  ScopedFILE scoped_file;
206 
207  if (source.is_stdin()) {
208  file = stdin;
209  } else {
210  ScopedFD fd = OpenFile(source.filename());
211  if (!fd) {
212  return false;
213  }
214 
215  scoped_file = FDToFILE(std::move(fd), "rb");
216  if (!scoped_file) {
217  perror("fdopen");
218  return false;
219  }
220  file = scoped_file.get();
221  }
222 
223  const size_t hex_size = EVP_MD_size(md) * 2;
224  char line[EVP_MAX_MD_SIZE * 2 + 2 /* spaces */ + PATH_MAX + 1 /* newline */ +
225  1 /* NUL */];
226  unsigned bad_lines = 0;
227  unsigned parsed_lines = 0;
228  unsigned error_lines = 0;
229  unsigned bad_hash_lines = 0;
230  unsigned line_no = 0;
231  bool ok = true;
232  bool draining_overlong_line = false;
233 
234  for (;;) {
235  line_no++;
236 
237  if (fgets(line, sizeof(line), file) == nullptr) {
238  if (feof(file)) {
239  break;
240  }
241  fprintf(stderr, "Error reading from input.\n");
242  return false;
243  }
244 
245  size_t len = strlen(line);
246 
247  if (draining_overlong_line) {
248  if (line[len - 1] == '\n') {
249  draining_overlong_line = false;
250  }
251  continue;
252  }
253 
254  const bool overlong = line[len - 1] != '\n' && !feof(file);
255 
256  if (len < hex_size + 2 /* spaces */ + 1 /* filename */ ||
257  line[hex_size] != ' ' ||
258  line[hex_size + 1] != ' ' ||
259  overlong) {
260  bad_lines++;
261  if (args.warn) {
262  fprintf(stderr, "%s: %u: improperly formatted line\n",
263  source.is_stdin() ? kStdinName : source.filename().c_str(), line_no);
264  }
265  if (args.strict) {
266  ok = false;
267  }
268  if (overlong) {
269  draining_overlong_line = true;
270  }
271  continue;
272  }
273 
274  if (line[len - 1] == '\n') {
275  line[len - 1] = 0;
276  len--;
277  }
278 
279  parsed_lines++;
280 
281  // coreutils does not attempt to restrict relative or absolute paths in the
282  // input so nor does this code.
283  std::string calculated_hex_digest;
284  const std::string target_filename(&line[hex_size + 2]);
285  Source target_source;
286  if (target_filename == "-") {
287  // coreutils reads from stdin if the filename is "-".
288  target_source = Source(Source::STDIN);
289  } else {
290  target_source = Source(target_filename);
291  }
292 
293  if (!SumFile(&calculated_hex_digest, md, target_source)) {
294  error_lines++;
295  ok = false;
296  continue;
297  }
298 
299  if (calculated_hex_digest != std::string(line, hex_size)) {
300  bad_hash_lines++;
301  if (!args.status) {
302  printf("%s: FAILED\n", target_filename.c_str());
303  }
304  ok = false;
305  continue;
306  }
307 
308  if (!args.quiet) {
309  printf("%s: OK\n", target_filename.c_str());
310  }
311  }
312 
313  if (!args.status) {
314  if (bad_lines > 0 && parsed_lines > 0) {
315  fprintf(stderr, "WARNING: %u line%s improperly formatted\n", bad_lines,
316  bad_lines == 1 ? " is" : "s are");
317  }
318  if (error_lines > 0) {
319  fprintf(stderr, "WARNING: %u computed checksum(s) did NOT match\n",
320  error_lines);
321  }
322  }
323 
324  if (parsed_lines == 0) {
325  fprintf(stderr, "%s: no properly formatted checksum lines found.\n",
326  source.is_stdin() ? kStdinName : source.filename().c_str());
327  ok = false;
328  }
329 
330  return ok;
331 }
332 
333 // DigestSum acts like the coreutils *sum utilites, with the given hash
334 // function.
335 static bool DigestSum(const EVP_MD *md,
336  const std::vector<std::string> &args) {
337  bool check_mode = false;
338  CheckModeArguments check_args;
339  bool check_mode_args_given = false;
340  std::vector<Source> sources;
341 
342  auto it = args.begin();
343  while (it != args.end()) {
344  const std::string &arg = *it;
345  if (!arg.empty() && arg[0] != '-') {
346  break;
347  }
348 
349  it++;
350 
351  if (arg == "--") {
352  break;
353  }
354 
355  if (arg == "-") {
356  // "-" ends the argument list and indicates that stdin should be used.
357  sources.push_back(Source(Source::STDIN));
358  break;
359  }
360 
361  if (arg.size() >= 2 && arg[0] == '-' && arg[1] != '-') {
362  for (size_t i = 1; i < arg.size(); i++) {
363  switch (arg[i]) {
364  case 'b':
365  case 't':
366  // Binary/text mode – irrelevent, even on Windows.
367  break;
368  case 'c':
369  check_mode = true;
370  break;
371  case 'w':
372  check_mode_args_given = true;
373  check_args.warn = true;
374  break;
375  default:
376  fprintf(stderr, "Unknown option '%c'.\n", arg[i]);
377  return false;
378  }
379  }
380  } else if (arg == "--binary" || arg == "--text") {
381  // Binary/text mode – irrelevent, even on Windows.
382  } else if (arg == "--check") {
383  check_mode = true;
384  } else if (arg == "--quiet") {
385  check_mode_args_given = true;
386  check_args.quiet = true;
387  } else if (arg == "--status") {
388  check_mode_args_given = true;
389  check_args.status = true;
390  } else if (arg == "--warn") {
391  check_mode_args_given = true;
392  check_args.warn = true;
393  } else if (arg == "--strict") {
394  check_mode_args_given = true;
395  check_args.strict = true;
396  } else {
397  fprintf(stderr, "Unknown option '%s'.\n", arg.c_str());
398  return false;
399  }
400  }
401 
402  if (check_mode_args_given && !check_mode) {
403  fprintf(
404  stderr,
405  "Check mode arguments are only meaningful when verifying checksums.\n");
406  return false;
407  }
408 
409  for (; it != args.end(); it++) {
410  sources.push_back(Source(*it));
411  }
412 
413  if (sources.empty()) {
414  sources.push_back(Source(Source::STDIN));
415  }
416 
417  bool ok = true;
418 
419  if (check_mode) {
420  for (auto &source : sources) {
421  ok &= Check(check_args, md, source);
422  }
423  } else {
424  for (auto &source : sources) {
425  ok &= PrintFileSum(md, source);
426  }
427  }
428 
429  return ok;
430 }
431 
432 bool MD5Sum(const std::vector<std::string> &args) {
433  return DigestSum(EVP_md5(), args);
434 }
435 
436 bool SHA1Sum(const std::vector<std::string> &args) {
437  return DigestSum(EVP_sha1(), args);
438 }
439 
440 bool SHA224Sum(const std::vector<std::string> &args) {
441  return DigestSum(EVP_sha224(), args);
442 }
443 
444 bool SHA256Sum(const std::vector<std::string> &args) {
445  return DigestSum(EVP_sha256(), args);
446 }
447 
448 bool SHA384Sum(const std::vector<std::string> &args) {
449  return DigestSum(EVP_sha384(), args);
450 }
451 
452 bool SHA512Sum(const std::vector<std::string> &args) {
453  return DigestSum(EVP_sha512(), args);
454 }
455 
456 bool SHA512256Sum(const std::vector<std::string> &args) {
457  return DigestSum(EVP_sha512_256(), args);
458 }
filename
const char * filename
Definition: bloaty/third_party/zlib/contrib/minizip/ioapi.h:135
internal.h
EVP_sha512
const OPENSSL_EXPORT EVP_MD * EVP_sha512(void)
regen-readme.it
it
Definition: regen-readme.py:15
ctx
Definition: benchmark-async.c:30
file
const grpc_generator::File * file
Definition: python_private_generator.h:38
env_md_st
Definition: third_party/boringssl-with-bazel/src/crypto/fipsmodule/digest/internal.h:67
false
#define false
Definition: setup_once.h:323
Source::Source
Source(Type)
Definition: digest.cc:56
EVP_sha384
const OPENSSL_EXPORT EVP_MD * EVP_sha384(void)
kStdinName
static const char kStdinName[]
Definition: digest.cc:68
MD5Sum
bool MD5Sum(const std::vector< std::string > &args)
Definition: digest.cc:432
CheckModeArguments::quiet
bool quiet
Definition: digest.cc:189
check_version.warning
string warning
Definition: check_version.py:46
string.h
buf
voidpf void * buf
Definition: bloaty/third_party/zlib/contrib/minizip/ioapi.h:136
printf
_Use_decl_annotations_ int __cdecl printf(const char *_Format,...)
Definition: cs_driver.c:91
testing::internal::string
::std::string string
Definition: bloaty/third_party/protobuf/third_party/googletest/googletest/include/gtest/internal/gtest-port.h:881
Source::Source
Source(const std::string &name)
Definition: digest.cc:57
file
Definition: bloaty/third_party/zlib/examples/gzappend.c:170
EVP_DigestUpdate
#define EVP_DigestUpdate
Definition: boringssl_prefix_symbols.h:1516
ctx
static struct test_ctx ctx
Definition: test-ipc-send-recv.c:65
setup.name
name
Definition: setup.py:542
EVP_DigestInit_ex
#define EVP_DigestInit_ex
Definition: boringssl_prefix_symbols.h:1511
EVP_sha256
const OPENSSL_EXPORT EVP_MD * EVP_sha256(void)
uint8_t
unsigned char uint8_t
Definition: stdint-msvc2008.h:78
ReadFromFD
bool ReadFromFD(int fd, size_t *out_bytes_read, void *out, size_t num)
Definition: fd.cc:53
demumble_test.stdin
stdin
Definition: demumble_test.py:37
base.h
true
#define true
Definition: setup_once.h:324
python_utils.port_server.stderr
stderr
Definition: port_server.py:51
CheckModeArguments::warn
bool warn
Definition: digest.cc:191
SHA512256Sum
bool SHA512256Sum(const std::vector< std::string > &args)
Definition: digest.cc:456
asyncio_get_stats.args
args
Definition: asyncio_get_stats.py:40
absl::move
constexpr absl::remove_reference_t< T > && move(T &&t) noexcept
Definition: abseil-cpp/absl/utility/utility.h:221
Check
static bool Check(const CheckModeArguments &args, const EVP_MD *md, const Source &source)
Definition: digest.cc:202
Type
Definition: bloaty/third_party/protobuf/src/google/protobuf/type.pb.h:182
DigestSum
static bool DigestSum(const EVP_MD *md, const std::vector< std::string > &args)
Definition: digest.cc:335
EVP_MD_size
#define EVP_MD_size
Definition: boringssl_prefix_symbols.h:1579
ScopedFD
Definition: third_party/boringssl-with-bazel/src/tool/internal.h:47
Source::filename
const std::string & filename() const
Definition: digest.cc:61
arg
Definition: cmdline.cc:40
FDToFILE
ScopedFILE FDToFILE(ScopedFD fd, const char *mode)
Definition: fd.cc:93
OPENSSL_MSVC_PRAGMA
OPENSSL_MSVC_PRAGMA(warning(disable:4702))
Definition: e_aes.c:69
CheckModeArguments::strict
bool strict
Definition: digest.cc:192
b
uint64_t b
Definition: abseil-cpp/absl/container/internal/layout_test.cc:53
OpenFile
static ScopedFD OpenFile(const std::string &filename)
Definition: digest.cc:72
n
int n
Definition: abseil-cpp/absl/container/btree_test.cc:1080
O_BINARY
#define O_BINARY
Definition: digest.cc:32
Source::filename_
std::string filename_
Definition: digest.cc:65
push
int push(void *desc, unsigned char *buf, unsigned len)
Definition: bloaty/third_party/zlib/test/infcover.c:463
PrintFileSum
static bool PrintFileSum(const EVP_MD *md, const Source &source)
Definition: digest.cc:167
SumFile
static bool SumFile(std::string *out_hex, const EVP_MD *md, const Source &source)
Definition: digest.cc:102
ScopedFD::get
int get() const
Definition: third_party/boringssl-with-bazel/src/tool/internal.h:65
Source::is_stdin
bool is_stdin() const
Definition: digest.cc:60
benchmark.md
md
Definition: benchmark.py:86
Source::Source
Source()
Definition: digest.cc:55
gen_build_yaml.sources
sources
Definition: src/boringssl/gen_build_yaml.py:27
ScopedFILE
std::unique_ptr< FILE, FileCloser > ScopedFILE
Definition: third_party/boringssl-with-bazel/src/tool/internal.h:39
digest.h
benchmark.FILE
FILE
Definition: benchmark.py:21
EVP_DigestFinal_ex
#define EVP_DigestFinal_ex
Definition: boringssl_prefix_symbols.h:1509
CheckModeArguments::status
bool status
Definition: digest.cc:190
SHA224Sum
bool SHA224Sum(const std::vector< std::string > &args)
Definition: digest.cc:440
EVP_MAX_MD_SIZE
#define EVP_MAX_MD_SIZE
Definition: digest.h:156
SHA512Sum
bool SHA512Sum(const std::vector< std::string > &args)
Definition: digest.cc:452
SHA384Sum
bool SHA384Sum(const std::vector< std::string > &args)
Definition: digest.cc:448
regen-readme.line
line
Definition: regen-readme.py:30
ok
bool ok
Definition: async_end2end_test.cc:197
CheckModeArguments
Definition: digest.cc:188
OpenFD
ScopedFD OpenFD(const char *path, int flags)
Definition: fd.cc:33
EVP_sha1
const OPENSSL_EXPORT EVP_MD * EVP_sha1(void)
stat
#define stat
Definition: test-fs.c:50
SHA256Sum
bool SHA256Sum(const std::vector< std::string > &args)
Definition: digest.cc:444
Source::STDIN
@ STDIN
Definition: digest.cc:52
len
int len
Definition: abseil-cpp/absl/base/internal/low_level_alloc_test.cc:46
EVP_md5
const OPENSSL_EXPORT EVP_MD * EVP_md5(void)
SHA1Sum
bool SHA1Sum(const std::vector< std::string > &args)
Definition: digest.cc:436
EVP_sha512_256
const OPENSSL_EXPORT EVP_MD * EVP_sha512_256(void)
Source
Definition: digest.cc:50
Source::is_stdin_
bool is_stdin_
Definition: digest.cc:64
EVP_sha224
const OPENSSL_EXPORT EVP_MD * EVP_sha224(void)
errno.h
i
uint64_t i
Definition: abseil-cpp/absl/container/btree_benchmark.cc:230


grpc
Author(s):
autogenerated on Thu Mar 13 2025 02:59:12