diff options
author | Francois Kritzinger <francois@codesynthesis.com> | 2024-02-28 10:52:08 +0200 |
---|---|---|
committer | Francois Kritzinger <francois@codesynthesis.com> | 2024-12-10 11:40:24 +0200 |
commit | eedef97cd9679ae68d7c989f194d957a35a00dd1 (patch) | |
tree | a4a4f5a4e6cee1b7ff5aea68f8619bc5a9d29c07 /mod/hmac.cxx | |
parent | abd6ede8444a89b6c56c20d06110cb3923b05bbe (diff) |
Verify webhook request HMACs
Diffstat (limited to 'mod/hmac.cxx')
-rw-r--r-- | mod/hmac.cxx | 95 |
1 files changed, 95 insertions, 0 deletions
diff --git a/mod/hmac.cxx b/mod/hmac.cxx new file mode 100644 index 0000000..1a78b4c --- /dev/null +++ b/mod/hmac.cxx @@ -0,0 +1,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 ()); + } +} |