aboutsummaryrefslogtreecommitdiff
path: root/mod/hmac.cxx
diff options
context:
space:
mode:
authorFrancois Kritzinger <francois@codesynthesis.com>2024-02-28 10:52:08 +0200
committerFrancois Kritzinger <francois@codesynthesis.com>2024-12-10 11:40:24 +0200
commiteedef97cd9679ae68d7c989f194d957a35a00dd1 (patch)
treea4a4f5a4e6cee1b7ff5aea68f8619bc5a9d29c07 /mod/hmac.cxx
parentabd6ede8444a89b6c56c20d06110cb3923b05bbe (diff)
Verify webhook request HMACs
Diffstat (limited to 'mod/hmac.cxx')
-rw-r--r--mod/hmac.cxx95
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 ());
+ }
+}