Harmonized behaviour when point length is invalid.
[BearSSL] / src / rand / sysrng.c
index 3a10db9..5a92114 100644 (file)
 #define BR_ENABLE_INTRINSICS   1
 #include "inner.h"
 
+#if BR_USE_GETENTROPY
+#include <unistd.h>
+#endif
+
 #if BR_USE_URANDOM
 #include <sys/types.h>
 #include <unistd.h>
@@ -38,6 +42,9 @@
 #pragma comment(lib, "advapi32")
 #endif
 
+/*
+ * Seeder that uses the RDRAND opcodes (on x86 CPU).
+ */
 #if BR_RDRAND
 BR_TARGETS_X86_UP
 BR_TARGET("rdrnd")
@@ -57,9 +64,24 @@ seeder_rdrand(const br_prng_class **ctx)
                 *
                 * Intel recommends trying at least 10 times in case of
                 * failure.
+                *
+                * AMD bug: there are reports that some AMD processors
+                * have a bug that makes them fail silently after a
+                * suspend/resume cycle, in which case RDRAND will report
+                * a success but always return 0xFFFFFFFF.
+                * see: https://bugzilla.kernel.org/show_bug.cgi?id=85911
+                *
+                * As a mitigation, if the 32-bit value is 0 or -1, then
+                * it is considered a failure and tried again. This should
+                * reliably detect the buggy case, at least. This also
+                * implies that the selected seed values can never be
+                * 0x00000000 or 0xFFFFFFFF, which is not a problem since
+                * we are generating a seed for a PRNG, and we overdo it
+                * a bit (we generate 32 bytes of randomness, and 256 bits
+                * of entropy are really overkill).
                 */
                for (j = 0; j < 10; j ++) {
-                       if (_rdrand32_step(&x)) {
+                       if (_rdrand32_step(&x) && x != 0 && x != (uint32_t)-1) {
                                goto next_word;
                        }
                }
@@ -80,9 +102,11 @@ rdrand_supported(void)
         */
        return br_cpuid(0, 0, 0x40000000, 0);
 }
-
 #endif
 
+/*
+ * Seeder that uses /dev/urandom (on Unix-like systems).
+ */
 #if BR_USE_URANDOM
 static int
 seeder_urandom(const br_prng_class **ctx)
@@ -116,6 +140,32 @@ seeder_urandom(const br_prng_class **ctx)
 }
 #endif
 
+/*
+ * Seeder that uses getentropy() (backed by getrandom() on some systems,
+ * e.g. Linux). On failure, it will use the /dev/urandom seeder (if
+ * enabled).
+ */
+#if BR_USE_GETENTROPY
+static int
+seeder_getentropy(const br_prng_class **ctx)
+{
+       unsigned char tmp[32];
+
+       if (getentropy(tmp, sizeof tmp) == 0) {
+               (*ctx)->update(ctx, tmp, sizeof tmp);
+               return 1;
+       }
+#if BR_USE_URANDOM
+       return seeder_urandom(ctx);
+#else
+       return 0;
+#endif
+}
+#endif
+
+/*
+ * Seeder that uses CryptGenRandom() (on Windows).
+ */
 #if BR_USE_WIN32_RAND
 static int
 seeder_win32(const br_prng_class **ctx)
@@ -139,7 +189,30 @@ seeder_win32(const br_prng_class **ctx)
 }
 #endif
 
-/* see bearssl_rand.h.h */
+/*
+ * An aggregate seeder that uses RDRAND, and falls back to an OS-provided
+ * source if RDRAND fails.
+ */
+#if BR_RDRAND && (BR_USE_GETENTROPY || BR_USE_URANDOM || BR_USE_WIN32_RAND)
+static int
+seeder_rdrand_with_fallback(const br_prng_class **ctx)
+{
+       if (!seeder_rdrand(ctx)) {
+#if BR_USE_GETENTROPY
+               return seeder_getentropy(ctx);
+#elif BR_USE_URANDOM
+               return seeder_urandom(ctx);
+#elif BR_USE_WIN32_RAND
+               return seeder_win32(ctx);
+#else
+#error "macro selection has gone wrong"
+#endif
+       }
+       return 1;
+}
+#endif
+
+/* see bearssl_rand.h */
 br_prng_seeder
 br_prng_seeder_system(const char **name)
 {
@@ -148,10 +221,19 @@ br_prng_seeder_system(const char **name)
                if (name != NULL) {
                        *name = "rdrand";
                }
+#if BR_USE_GETENTROPY || BR_USE_URANDOM || BR_USE_WIN32_RAND
+               return &seeder_rdrand_with_fallback;
+#else
                return &seeder_rdrand;
+#endif
        }
 #endif
-#if BR_USE_URANDOM
+#if BR_USE_GETENTROPY
+       if (name != NULL) {
+               *name = "getentropy";
+       }
+       return &seeder_getentropy;
+#elif BR_USE_URANDOM
        if (name != NULL) {
                *name = "urandom";
        }
@@ -161,9 +243,10 @@ br_prng_seeder_system(const char **name)
                *name = "win32";
        }
        return &seeder_win32;
-#endif
+#else
        if (name != NULL) {
                *name = "none";
        }
        return 0;
+#endif
 }