diff --git a/examples/client.cc b/examples/client.cc
index 5eafef1d0f3ca0f088dd07e8c9bb67ca60b6b1b4..4a11123203d49347f557518dfc1c0a20c9aff5d0 100644
--- a/examples/client.cc
+++ b/examples/client.cc
@@ -621,7 +621,8 @@ int extend_max_streams_bidi(ngtcp2_conn *conn, uint64_t max_streams,
 } // namespace
 
 namespace {
-int rand(uint8_t *dest, size_t destlen, ngtcp2_rand_usage usage) {
+int rand(uint8_t *dest, size_t destlen, const ngtcp2_rand_ctx *rand_ctx,
+         ngtcp2_rand_usage usage) {
   auto dis = std::uniform_int_distribution<uint8_t>(0, 255);
   std::generate(dest, dest + destlen, [&dis]() { return dis(randgen); });
   return 0;
diff --git a/examples/h09client.cc b/examples/h09client.cc
index b8484316f062c0565c90615e635811036d56bcba..02c40d3810e9057e60359690d82202cd395c9169 100644
--- a/examples/h09client.cc
+++ b/examples/h09client.cc
@@ -597,7 +597,8 @@ int extend_max_streams_bidi(ngtcp2_conn *conn, uint64_t max_streams,
 } // namespace
 
 namespace {
-int rand(uint8_t *dest, size_t destlen, ngtcp2_rand_usage usage) {
+int rand(uint8_t *dest, size_t destlen, const ngtcp2_rand_ctx *rand_ctx,
+         ngtcp2_rand_usage usage) {
   auto dis = std::uniform_int_distribution<uint8_t>(0, 255);
   std::generate(dest, dest + destlen, [&dis]() { return dis(randgen); });
   return 0;
diff --git a/examples/h09server.cc b/examples/h09server.cc
index 8e49c9a34e849cf0f75da49ba95f1fd28c180df1..684a49d533b148ae9f76a09082d51fc48eaee480 100644
--- a/examples/h09server.cc
+++ b/examples/h09server.cc
@@ -704,7 +704,8 @@ int stream_close(ngtcp2_conn *conn, int64_t stream_id, uint64_t app_error_code,
 } // namespace
 
 namespace {
-int rand(uint8_t *dest, size_t destlen, ngtcp2_rand_usage usage) {
+int rand(uint8_t *dest, size_t destlen, const ngtcp2_rand_ctx *rand_ctx,
+         ngtcp2_rand_usage usage) {
   auto dis = std::uniform_int_distribution<uint8_t>(0, 255);
   std::generate(dest, dest + destlen, [&dis]() { return dis(randgen); });
   return 0;
diff --git a/examples/server.cc b/examples/server.cc
index 427c57864588d2388f222c6ccf5e8db6cfaf96ff..cb2e1e3c7c703a520e6bab1aa54cee7651a62e0d 100644
--- a/examples/server.cc
+++ b/examples/server.cc
@@ -1064,7 +1064,8 @@ int Handler::on_stream_reset(int64_t stream_id) {
 }
 
 namespace {
-int rand(uint8_t *dest, size_t destlen, ngtcp2_rand_usage usage) {
+int rand(uint8_t *dest, size_t destlen, const ngtcp2_rand_ctx *rand_ctx,
+         ngtcp2_rand_usage usage) {
   auto dis = std::uniform_int_distribution<uint8_t>(0, 255);
   std::generate(dest, dest + destlen, [&dis]() { return dis(randgen); });
   return 0;
diff --git a/lib/includes/ngtcp2/ngtcp2.h b/lib/includes/ngtcp2/ngtcp2.h
index 88d781b2a1711bfd28cb4e9eba13f764eb0e21fb..11f496598569d5707037e41b66ad94ad2a4ac03e 100644
--- a/lib/includes/ngtcp2/ngtcp2.h
+++ b/lib/includes/ngtcp2/ngtcp2.h
@@ -711,6 +711,18 @@ typedef enum ngtcp2_qlog_write_flag {
   NGTCP2_QLOG_WRITE_FLAG_FIN = 0x01
 } ngtcp2_qlog_write_flag;
 
+/**
+ * @struct
+ *
+ * `ngtcp2_rand_ctx` is a wrapper around native random number
+ * generator.  It is opaque to the ngtcp2 library.  This might be
+ * useful if application needs to specify random number generator per
+ * thread or per connection.
+ */
+typedef struct ngtcp2_rand_ctx {
+  void *native_handle;
+} ngtcp2_rand_ctx;
+
 /**
  * @functypedef
  *
@@ -762,6 +774,11 @@ typedef struct ngtcp2_settings {
    * of token.
    */
   ngtcp2_vec token;
+  /**
+   * rand_ctx is an optional random number generator to be passed to
+   * :type:`ngtcp2_rand` callback.
+   */
+  ngtcp2_rand_ctx rand_ctx;
 } ngtcp2_settings;
 
 /**
@@ -1531,6 +1548,7 @@ typedef int (*ngtcp2_extend_max_stream_data)(ngtcp2_conn *conn,
  * immediately.
  */
 typedef int (*ngtcp2_rand)(uint8_t *dest, size_t destlen,
+                           const ngtcp2_rand_ctx *rand_ctx,
                            ngtcp2_rand_usage usage);
 
 /**
diff --git a/lib/ngtcp2_conn.c b/lib/ngtcp2_conn.c
index 7f30cdad2a7dae7dde894e40fa3bcba413c9c97a..6855544e00c68cb1126a12419f582c6218bf34be 100644
--- a/lib/ngtcp2_conn.c
+++ b/lib/ngtcp2_conn.c
@@ -3616,9 +3616,9 @@ static ngtcp2_ssize conn_write_path_challenge(ngtcp2_conn *conn,
   }
 
   assert(conn->callbacks.rand);
-  rv = conn->callbacks.rand(lfr.path_challenge.data,
-                            sizeof(lfr.path_challenge.data),
-                            NGTCP2_RAND_USAGE_PATH_CHALLENGE);
+  rv = conn->callbacks.rand(
+      lfr.path_challenge.data, sizeof(lfr.path_challenge.data),
+      &conn->local.settings.rand_ctx, NGTCP2_RAND_USAGE_PATH_CHALLENGE);
   if (rv != 0) {
     return NGTCP2_ERR_CALLBACK_FAILURE;
   }
diff --git a/tests/ngtcp2_conn_test.c b/tests/ngtcp2_conn_test.c
index f17584568245c305ee1762497aea3cafdad76e9c..1fa0b9e1b117a7ec9438095772c781ea211a92bf 100644
--- a/tests/ngtcp2_conn_test.c
+++ b/tests/ngtcp2_conn_test.c
@@ -341,7 +341,9 @@ static int recv_retry(ngtcp2_conn *conn, const ngtcp2_pkt_hd *hd,
   return 0;
 }
 
-static int genrand(uint8_t *dest, size_t destlen, ngtcp2_rand_usage usage) {
+static int genrand(uint8_t *dest, size_t destlen,
+                   const ngtcp2_rand_ctx *rand_ctx, ngtcp2_rand_usage usage) {
+  (void)rand_ctx;
   (void)usage;
 
   memset(dest, 0, destlen);