aboutsummaryrefslogtreecommitdiff
path: root/mod/hmac.cxx
blob: 1a78b4cc2ef5a692ed314003f8167a6d2139680c (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
#include <mod/hmac.hxx>

#include <libbutl/openssl.hxx>

using namespace std;
using namespace butl;

string brep::
compute_hmac (const options::openssl_options& o,
              const void* m, size_t l,
              const char* k)
{
  try
  {
    fdpipe errp (fdopen_pipe ()); // stderr pipe.

    // To compute an HMAC over stdin with the key <secret>:
    //
    //   openssl mac -digest SHA256 -macopt "key:<secret>" HMAC
    //
    // Note that here we assume both output and diagnostics will fit into pipe
    // buffers and don't poll both with fdselect().
    //
    openssl os (path ("-"), // Read message from openssl::out.
                path ("-"), // Write output to openssl::in.
                process::pipe (errp.in.get (), move (errp.out)),
                process_env (o.openssl (), o.openssl_envvar ()),
                "mac", o.openssl_option (),
                "-digest", "SHA256",
                "-macopt", string ("key:") + k,
                "HMAC");

    ifdstream err (move (errp.in));

    string h; // The HMAC value.
    try
    {
      // In case of an exception, skip and close input after output.
      //
      // Note: re-open in/out so that they get automatically closed on
      // an exception.
      //
      ifdstream in (os.in.release (), fdstream_mode::skip);
      ofdstream out (os.out.release ());

      // Write the message to openssl's input.
      //
      out.write (static_cast<const char*> (m), l);
      out.close ();

      // Read the HMAC value from openssl's output.
      //
      h = in.read_text ();
      in.close ();
    }
    catch (const io_error& e)
    {
      // If the process exits with non-zero status, assume the IO error is due
      // to that and fall through.
      //
      if (os.wait ())
      {
        throw_generic_error (
          e.code ().value (),
          (string ("unable to read/write openssl stdout/stdin: ") +
           e.what ()).c_str ());
      }
    }

    if (!os.wait ())
    {
      string et (err.read_text ());
      throw_generic_error (EINVAL,
                           ("non-zero openssl exit status: " + et).c_str ());
    }

    err.close ();

    return h;
  }
  catch (const process_error& e)
  {
    throw_generic_error (
      e.code ().value (),
      (string ("unable to execute openssl: ") + e.what ()).c_str ());
  }
  catch (const io_error& e)
  {
    // Unable to read diagnostics from stderr.
    //
    throw_generic_error (
      e.code ().value (),
      (string ("unable to read openssl stderr : ") + e.what ()).c_str ());
  }
}