Repository: bitcoin/bitcoin (language: cpp)

Issue:
Request support for HRNG on ARM using their new RNDR / RNDRRS instructions.
Bitcoin Core currently only support to using RDRAND / RDSEED instruction on x86 architecture when requiring to generate a ultra high quality random numbers. For example, a private key.
https://github.com/bitcoin/bitcoin/blob/master/src/random.h
https://github.com/bitcoin/bitcoin/blob/master/src/random.cpp

ARM later added two new similar instructions as well, RNDR (similar to RDRAND) / RNDRRS (similar to RDSEED).
https://developer.arm.com/documentation/ddi0601/2022-12/AArch64-Registers/RNDR--Random-Number
https://developer.arm.com/documentation/ddi0601/2022-12/AArch64-Registers/RNDRRS--Reseeded-Random-Number

OpenSSL also had a similar feature request on last year. (Implemented)
https://github.com/openssl/openssl/issues/15627
https://github.com/openssl/openssl/pull/15361
https://github.com/openssl/openssl/blob/master/providers/implementations/rands/seeding/rand_cpu_arm64.c

I hope this feature is also available on Bitcoin Core as Bitcoin Core also can using on ARM machine (E.g. A Mac computer using Apple silicon, Raspberry Pi...).


Discussion / hints:
It appears that these extensions aren’t supported by many [hardware implementations](https://marcin.juszkiewicz.com.pl/download/tables/arm-socs.html) yet (look under the ’rng’ column), including any Apple product or Raspberry Pi. 

That said, it would be a useful addition once they become more common. 

Fix patch:
diff --git a/src/random.cpp b/src/random.cpp
index 8e04d449f3490..187df16c36265 100644
--- a/src/random.cpp
+++ b/src/random.cpp
@@ -40,6 +40,9 @@
 #ifdef HAVE_SYSCTL_ARND
 #include <sys/sysctl.h>
 #endif
+#if defined(HAVE_STRONG_GETAUXVAL) && defined(__aarch64__)
+#include <sys/auxv.h>
+#endif
 
 [[noreturn]] static void RandFailure()
 {
@@ -175,6 +178,62 @@ static uint64_t GetRdSeed() noexcept
 #endif
 }
 
+#elif defined(__aarch64__) && defined(HWCAP2_RNG)
+
+static bool g_rndr_supported = false;
+
+static void InitHardwareRand()
+{
+    if (getauxval(AT_HWCAP2) & HWCAP2_RNG) {
+        g_rndr_supported = true;
+    }
+}
+
+static void ReportHardwareRand()
+{
+    // This must be done in a separate function, as InitHardwareRand() may be indirectly called
+    // from global constructors, before logging is initialized.
+    if (g_rndr_supported) {
+        LogPrintf("Using RNDR and RNDRRS as additional entropy sources\n");
+    }
+}
+
+/** Read 64 bits of entropy using rndr.
+ *
+ * Must only be called when RNDR is supported.
+ */
+static uint64_t GetRNDR() noexcept
+{
+    uint8_t ok;
+    uint64_t r1;
+    do {
+        // https://developer.arm.com/documentation/ddi0601/2022-12/AArch64-Registers/RNDR--Random-Number
+        __asm__ volatile("mrs %0, s3_3_c2_c4_0; cset %w1, ne;"
+                         : "=r"(r1), "=r"(ok)::"cc");
+        if (ok) break;
+        __asm__ volatile("yield");
+    } while (true);
+    return r1;
+}
+
+/** Read 64 bits of entropy using rndrrs.
+ *
+ * Must only be called when RNDRRS is supported.
+ */
+static uint64_t GetRNDRRS() noexcept
+{
+    uint8_t ok;
+    uint64_t r1;
+    do {
+        // https://developer.arm.com/documentation/ddi0601/2022-12/AArch64-Registers/RNDRRS--Reseeded-Random-Number
+        __asm__ volatile("mrs %0, s3_3_c2_c4_1; cset %w1, ne;"
+                         : "=r"(r1), "=r"(ok)::"cc");
+        if (ok) break;
+        __asm__ volatile("yield");
+    } while (true);
+    return r1;
+}
+
 #else
 /* Access to other hardware random number generators could be added here later,
  * assuming it is sufficiently fast (in the order of a few hundred CPU cycles).
@@ -193,6 +252,12 @@ static void SeedHardwareFast(CSHA512& hasher) noexcept {
         hasher.Write((const unsigned char*)&out, sizeof(out));
         return;
     }
+#elif defined(__aarch64__) && defined(HWCAP2_RNG)
+    if (g_rndr_supported) {
+        uint64_t out = GetRNDR();
+        hasher.Write((const unsigned char*)&out, sizeof(out));
+        return;
+    }
 #endif
 }
 
@@ -218,6 +283,14 @@ static void SeedHardwareSlow(CSHA512& hasher) noexcept {
         }
         return;
     }
+#elif defined(__aarch64__) && defined(HWCAP2_RNG)
+    if (g_rndr_supported) {
+        for (int i = 0; i < 4; ++i) {
+            uint64_t out = GetRNDRRS();
+            hasher.Write((const unsigned char*)&out, sizeof(out));
+        }
+        return;
+    }
 #endif
 }
 


Test patch: