Module Name: src
Committed By: riastradh
Date: Mon Mar 10 21:21:32 UTC 2025
Modified Files:
src/lib/libc/gen: arc4random.c
Log Message:
arc4random(3): Add self-tests for PRNG algorithm.
Independently generated by running:
head -c 64 </dev/zero \
| openssl enc -chacha -20 \
-K 000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f \
-iv -iv 00000000000000000000000000000000 \
| hexdump -C
and then repeating with the first 32 bytes of output as the updated
key for -K, extracting bytes [32, 32 + n) for the n-byte output in
each call.
To generate a diff of this commit:
cvs rdiff -u -r1.46 -r1.47 src/lib/libc/gen/arc4random.c
Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.
Modified files:
Index: src/lib/libc/gen/arc4random.c
diff -u src/lib/libc/gen/arc4random.c:1.46 src/lib/libc/gen/arc4random.c:1.47
--- src/lib/libc/gen/arc4random.c:1.46 Sun Mar 9 18:11:55 2025
+++ src/lib/libc/gen/arc4random.c Mon Mar 10 21:21:32 2025
@@ -1,4 +1,4 @@
-/* $NetBSD: arc4random.c,v 1.46 2025/03/09 18:11:55 riastradh Exp $ */
+/* $NetBSD: arc4random.c,v 1.47 2025/03/10 21:21:32 riastradh Exp $ */
/*-
* Copyright (c) 2014 The NetBSD Foundation, Inc.
@@ -51,7 +51,7 @@
*/
#include <sys/cdefs.h>
-__RCSID("$NetBSD: arc4random.c,v 1.46 2025/03/09 18:11:55 riastradh Exp $");
+__RCSID("$NetBSD: arc4random.c,v 1.47 2025/03/10 21:21:32 riastradh Exp $");
#include "namespace.h"
#include "reentrant.h"
@@ -319,6 +319,52 @@ crypto_prng_buf(struct crypto_prng *prng
(void)explicit_memset(output, 0, sizeof output);
}
+static int
+crypto_prng_selftest(void)
+{
+ const uint8_t expected[32] = {
+#if _BYTE_ORDER == _LITTLE_ENDIAN
+# if crypto_core_ROUNDS == 20
+ 0x2b, /* first call */
+ 0x2d,0x41,0xa5,0x9c,0x90,0xe4,0x1a,0x8e, /* second call */
+ 0x7a,0x4d,0xcc,0xaa,0x1c,0x46,0x06,0x99,
+ 0x83,0xb1,0xa3,0x33,0xce,0x25,0x71,0x9e,
+ 0xc3,0x43,0x77,0x68,0xab,0x57,
+ 0x5f, /* third call */
+# else
+# error crypto_core_ROUNDS other than 20 left as exercise for reader.
+# endif
+#elif _BYTE_ORDER == _BIG_ENDIAN
+# if crypto_core_ROUNDS == 20
+ 0xae, /* first call */
+ 0x97,0x14,0x5a,0x05,0xad,0xa8,0x48,0xf1, /* second call */
+ 0x3a,0x81,0x84,0xd7,0x05,0xda,0x20,0x5d,
+ 0xc0,0xef,0x86,0x65,0x98,0xbd,0xb0,0x16,
+ 0x1b,0xfc,0xff,0xc4,0xc2,0xfd,
+ 0xa0, /* third call */
+# else
+# error crypto_core_ROUNDS other than 20 left as exercise for reader.
+# endif
+#else
+# error Byte order must be little-endian or big-endian.
+#endif
+ };
+ uint8_t seed[crypto_prng_SEEDBYTES];
+ struct crypto_prng prng;
+ uint8_t output[32];
+ unsigned i;
+
+ for (i = 0; i < __arraycount(seed); i++)
+ seed[i] = i;
+ crypto_prng_seed(&prng, seed);
+ crypto_prng_buf(&prng, output, 1);
+ crypto_prng_buf(&prng, output + 1, 30);
+ crypto_prng_buf(&prng, output + 31, 1);
+ if (memcmp(output, expected, 32) != 0)
+ return EIO;
+ return 0;
+}
+
/* One-time stream: expand short single-use secret into long secret */
#define crypto_onetimestream_SEEDBYTES crypto_core_KEYBYTES
@@ -386,6 +432,60 @@ crypto_onetimestream(const void *seed, v
(void)explicit_memset(block, 0, sizeof block);
}
+static int
+crypto_onetimestream_selftest(void)
+{
+ const uint8_t expected[70] = {
+ 0x5a, /* guard byte */
+#if _BYTE_ORDER == _LITTLE_ENDIAN
+# if crypto_core_ROUNDS == 20
+ 0x39,0xfd,0x2b, /* initial block */
+ 0x18,0xb8,0x42,0x31,0xad,0xe6,0xa6,0xd1,
+ 0x13,0x61,0x5c,0x61,0xaf,0x43,0x4e,0x27,
+ 0xf8,0xb1,0xf3,0xf5,0xe1,0xad,0x5b,0x5c,
+ 0xec,0xf8,0xfc,0x12,0x2a,0x35,0x75,0x5c,
+ 0x72,0x08,0x08,0x6d,0xd1,0xee,0x3c,0x5d,
+ 0x9d,0x81,0x58,0x24,0x64,0x0e,0x00,0x3c,
+ 0x9b,0xa0,0xf6,0x5e,0xde,0x5d,0x59,0xce,
+ 0x0d,0x2a,0x4a,0x7f,0x31,0x95,0x5a,0xcd,
+ 0x42, /* final block */
+# else
+# error crypto_core_ROUNDS other than 20 left as exercise for reader.
+# endif
+#elif _BYTE_ORDER == _BIG_ENDIAN
+# if crypto_core_ROUNDS == 20
+ 0x20,0xf0,0x66, /* initial block */
+ 0xc9,0x06,0x63,0xc5,0x45,0x38,0xd1,0xb1,
+ 0xe6,0x3e,0xbf,0x68,0x19,0xd6,0xf1,0xbe,
+ 0x09,0xb9,0x49,0xc4,0xf5,0x55,0x95,0xc1,
+ 0x54,0x56,0xeb,0xe4,0x8c,0xa5,0xbb,0x55,
+ 0x17,0x89,0x8e,0x90,0x51,0x53,0xea,0x17,
+ 0x29,0xf5,0x7e,0xe4,0x78,0x08,0x53,0xc8,
+ 0x54,0xa8,0xba,0x76,0xce,0x0e,0x8d,0x2f,
+ 0xe1,0x07,0xc8,0x46,0x73,0x3e,0x61,0x0c,
+ 0x02, /* final block */
+# else
+# error crypto_core_ROUNDS other than 20 left as exercise for reader.
+# endif
+#else
+# error Byte order must be little-endian or big-endian.
+#endif
+ 0xcc, /* guard byte */
+ };
+ uint8_t seed[crypto_prng_SEEDBYTES];
+ uint8_t output[70] __aligned(4);
+ unsigned i;
+
+ for (i = 0; i < __arraycount(seed); i++)
+ seed[i] = i;
+ output[0] = 0x5a;
+ output[69] = 0xcc;
+ crypto_onetimestream(seed, output + 1, 68);
+ if (memcmp(output, expected, 70) != 0)
+ return EIO;
+ return 0;
+}
+
/*
* entropy_epoch()
*
@@ -561,7 +661,9 @@ arc4random_initialize(void)
* If the crypto software is broken, abort -- something is
* severely wrong with this process image.
*/
- if (crypto_core_selftest() != 0)
+ if (crypto_core_selftest() != 0 ||
+ crypto_prng_selftest() != 0 ||
+ crypto_onetimestream_selftest() != 0)
abort();
/*