Subject: Re: bug#58472: [PATCH] Make `message-unique-id' less prone to collisions

From: Paul Eggert <[email-masked]>
Date: Mon, 17 Oct 2022 00:30:49 -0700

I've been looking into this and have several patches along these lines. 
None of them address message-unique-id directly yet (I plan to tackle 
this soon) but they do address the general problem area. The basic idea 
is to use a new make-nonce primitive.

I thought I'd email the patches now to see what others think about the 
direction they're headed.
From 80c18cb40c3c9ddd56e88019387a3827d0f6add0 Mon Sep 17 00:00:00 2001
From: Paul Eggert <[email-masked]>
Date: Sun, 16 Oct 2022 10:48:29 -0700
Subject: [PATCH 01/10] No need to call w32_init_random
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* src/sysdep.c (init_random): Just use getrandom,
since we’re already using it unconditionally elsewhere.
---
 src/sysdep.c | 8 +-------
 1 file changed, 1 insertion(+), 7 deletions(-)

diff --git a/src/sysdep.c b/src/sysdep.c
index 736723bdf3..4786c8fa4f 100644
--- a/src/sysdep.c
+++ b/src/sysdep.c
@@ -2163,17 +2163,11 @@ seed_random (void *seed, ptrdiff_t seed_size)
 init_random (void)
 {
   random_seed v;
-  bool success = false;
 
   /* First, try seeding the PRNG from the operating system's entropy
      source.  This approach is both fast and secure.  */
-#ifdef WINDOWSNT
-  /* FIXME: Perhaps getrandom can be used here too?  */
-  success = w32_init_random (&v, sizeof v) == 0;
-#else
   verify (sizeof v <= 256);
-  success = getrandom (&v, sizeof v, 0) == sizeof v;
-#endif
+  bool success = getrandom (&v, sizeof v, 0) == sizeof v;
 
   /* If that didn't work, just use the current time value and PID.
      It's at least better than XKCD 221.  */
-- 
2.34.1

From a793ed61e6ed216d15a012deb980301d0e61fd43 Mon Sep 17 00:00:00 2001
From: Paul Eggert <[email-masked]>
Date: Sun, 16 Oct 2022 10:48:29 -0700
Subject: [PATCH 02/10] Simplify feedmail Message-ID generation
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* lisp/mail/feedmail.el (feedmail-default-message-id-generator):
Avoid an unnecessary call to ‘mod’.
---
 lisp/mail/feedmail.el | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/lisp/mail/feedmail.el b/lisp/mail/feedmail.el
index 2ae916e3ac..047c8f404e 100644
--- a/lisp/mail/feedmail.el
+++ b/lisp/mail/feedmail.el
@@ -2819,7 +2819,7 @@ feedmail-default-message-id-generator
 	(setq date-time (file-attribute-modification-time
 			 (file-attributes maybe-file))))
     (format "<%d-%s%s>"
-	    (mod (random) 10000)
+	    (random 10000)
 	    (format-time-string "%a%d%b%Y%H%M%S%z" date-time)
 	    end-stuff))
   )
-- 
2.34.1

From 318c88aaf1033d452a5449eba45aa382988412f7 Mon Sep 17 00:00:00 2001
From: Paul Eggert <[email-masked]>
Date: Sun, 16 Oct 2022 23:35:21 -0700
Subject: [PATCH 03/10] =?UTF-8?q?Simplify=20calc-comb=20by=20using=20?=
 =?UTF-8?q?=E2=80=98random=E2=80=99?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* lisp/calc/calc-comb.el (math-random-table, math-last-RandSeed)
(math-random-ptr1, math-random-ptr2, math-random-shift)
(var-RandSeed, math-random-cache, math-init-random-base)
(math-random-base, math-random-last)
(math-random-three-digit-number):
Now obsolete, as we can assume that ‘random’ is good enough.
(math-random-digits): Simplify by using ‘random’.
---
 lisp/calc/calc-comb.el  | 96 ++++++++---------------------------------
 lisp/calc/calc-ext.el   |  6 ++-
 lisp/calc/calc-stuff.el |  4 +-
 3 files changed, 23 insertions(+), 83 deletions(-)

diff --git a/lisp/calc/calc-comb.el b/lisp/calc/calc-comb.el
index c1352fa324..522a7ac6c0 100644
--- a/lisp/calc/calc-comb.el
+++ b/lisp/calc/calc-comb.el
@@ -546,105 +546,42 @@ math-stirling-2
 		      (math-mul m (math-stirling-2 (1- n) m))))))
 
 (defvar math-random-table nil)
+(make-obsolete-variable 'math-random-table nil "29.1")
 (defvar math-last-RandSeed nil)
+(make-obsolete-variable 'math-last-RandSeed nil "29.1")
 (defvar math-random-ptr1 nil)
+(make-obsolete-variable 'math-random-ptr1 nil "29.1")
 (defvar math-random-ptr2 nil)
-(defvar math-random-shift nil)
+(make-obsolete-variable 'math-random-ptr2 nil "29.1")
+(defvar math-random-shift -4)	; assume RAND_MAX >= 16383
+(make-obsolete-variable 'math-random-shift nil "29.1")
 
-;;; Produce a random 10-bit integer, with (random) if no seed provided,
-;;; or else with Numerical Recipes algorithm ran3 / Knuth 3.2.2-A.
+;;; Produce a random 10-bit integer.
 
 (defvar var-RandSeed)
+(make-obsolete-variable 'var-RandSeed nil "29.1")
 (defvar math-random-cache nil)
-(defvar math-gaussian-cache nil)
+(make-obsolete-variable 'math-random-cache nil "29.1")
 
 (defun math-init-random-base ()
-  (if (and (boundp 'var-RandSeed) var-RandSeed)
-      (if (eq (car-safe var-RandSeed) 'vec)
-	  nil
-	(if (Math-integerp var-RandSeed)
-	    (let* ((seed (math-sub 161803 var-RandSeed))
-		   (mj (1+ (math-mod seed 1000000)))
-		   (mk (1+ (math-mod (math-quotient seed 1000000)
-                                     1000000)))
-		   (i 0))
-	      (setq math-random-table (cons 'vec (make-list 55 mj)))
-	      (while (<= (setq i (1+ i)) 54)
-		(let* ((ii (% (* i 21) 55))
-		       (p (nthcdr ii math-random-table)))
-		  (setcar p mk)
-		  (setq mk (- mj mk)
-			mj (car p)))))
-	  (math-reject-arg var-RandSeed "*RandSeed must be an integer"))
-	(setq var-RandSeed (list 'vec var-RandSeed)
-	      math-random-ptr1 math-random-table
-	      math-random-cache nil
-	      math-random-ptr2 (nthcdr 31 math-random-table))
-	(let ((i 200))
-	  (while (> (setq i (1- i)) 0)
-	    (math-random-base))))
-    (setq var-RandSeed nil
-	  math-random-cache nil
-	  math-random-shift -4)  ; assume RAND_MAX >= 16383
-    ;; This exercises the random number generator and also helps
-    ;; deduce a better value for RAND_MAX.
-    (let ((i 0))
-      (while (< (setq i (1+ i)) 30)
-        (if (> (ash (math-abs (random)) math-random-shift) 4095)
-            (setq math-random-shift (1- math-random-shift))))))
-  (setq math-last-RandSeed var-RandSeed
-	math-gaussian-cache nil))
+  (declare (obsolete nil "29.1")))
 
 (defun math-random-base ()
-  (if var-RandSeed
-      (progn
-	(setq math-random-ptr1 (or (cdr math-random-ptr1)
-				   (cdr math-random-table))
-	      math-random-ptr2 (or (cdr math-random-ptr2)
-				   (cdr math-random-table)))
-	(logand (ash (setcar math-random-ptr1
-			     (logand (- (car math-random-ptr1)
-					(car math-random-ptr2)) 524287))
-		     -6) 1023))
-    (logand (ash (random) math-random-shift) 1023)))
-
+  (declare (obsolete 'random "29.1"))
+  (random 1024))
 
 ;;; Produce a random digit in the range 0..999.
-;;; Avoid various pitfalls that may lurk in the built-in (random) function!
-;;; Shuffling algorithm from Numerical Recipes, section 7.1.
 (defvar math-random-last)
+(make-obsolete-variable 'math-random-last nil "29.1")
 (defun math-random-three-digit-number ()
   "Return a random three digit number."
-  (let (i)
-    (or (and (boundp 'var-RandSeed) (eq var-RandSeed math-last-RandSeed))
-	(math-init-random-base))
-    (or math-random-cache
-	(progn
-	  (setq math-random-last (math-random-base)
-		math-random-cache (make-vector 13 nil)
-		i -1)
-	  (while (< (setq i (1+ i)) 13)
-	    (aset math-random-cache i (math-random-base)))))
-    (while (progn
-	     (setq i (/ math-random-last 79)   ; 0 <= i < 13
-		   math-random-last (aref math-random-cache i))
-	     (aset math-random-cache i (math-random-base))
-	     (>= math-random-last 1000)))
-    math-random-last))
+  (declare (obsolete 'random "29.1"))
+  (random 1000))
 
 ;;; Produce an N-digit random integer.
 (defun math-random-digits (n)
   "Produce a random N digit integer."
-  (let* ((slop (% (- 3 (% n 3)) 3))
-         (i (/ (+ n slop) 3))
-         (rnum 0))
-    (while (> i 0)
-      (setq rnum
-            (math-add
-             (math-random-three-digit-number)
-             (math-mul rnum 1000)))
-      (setq i (1- i)))
-    (math-normalize (math-scale-right rnum slop))))
+  (random (expt 10 n)))
 
 ;;; Produce a uniformly-distributed random float 0 <= N < 1.
 (defun math-random-float ()
@@ -652,6 +589,7 @@ math-random-float
 		   (- calc-internal-prec)))
 
 ;;; Produce a Gaussian-distributed random float with mean=0, sigma=1.
+(defvar math-gaussian-cache nil)
 (defun math-gaussian-float ()
   (math-with-extra-prec 2
     (if (and math-gaussian-cache
diff --git a/lisp/calc/calc-ext.el b/lisp/calc/calc-ext.el
index 7ee73d100a..55808d28b1 100644
--- a/lisp/calc/calc-ext.el
+++ b/lisp/calc/calc-ext.el
@@ -785,8 +785,10 @@ calc-init-extensions
 calcFunc-gcd calcFunc-lcm calcFunc-moebius calcFunc-nextprime
 calcFunc-perm calcFunc-prevprime calcFunc-prfac calcFunc-prime
 calcFunc-random calcFunc-shuffle calcFunc-stir1 calcFunc-stir2
-calcFunc-totient math-init-random-base math-member math-prime-test
-math-random-base)
+calcFunc-totient
+math-init-random-base ; obsolescent as of 29.1
+math-random-base ; obsolescent as of 29.1
+math-member math-prime-test)
 
  ("calccomp" calcFunc-cascent calcFunc-cdescent
 calcFunc-cheight calcFunc-cwidth math-comp-ascent math-comp-descent
diff --git a/lisp/calc/calc-stuff.el b/lisp/calc/calc-stuff.el
index 758b920184..87a7eb6e65 100644
--- a/lisp/calc/calc-stuff.el
+++ b/lisp/calc/calc-stuff.el
@@ -155,7 +155,7 @@ math-lud-cache
 (defvar math-log2-cache) ; calc-bin.el
 (defvar math-radix-digits-cache) ; calc-bin.el
 (defvar math-radix-float-cache-tag) ; calc-bin.el
-(defvar math-random-cache) ; calc-comb.el
+(defvar math-random-cache) ; calc-comb.el; obsolescent as of 29.1
 (defvar math-max-digits-cache) ; calc-bin.el
 (defvar math-integral-cache) ; calcalg2.el
 (defvar math-units-table) ; calc-units.el
@@ -170,7 +170,7 @@ calc-flush-caches
 	 math-log2-cache nil
 	 math-radix-digits-cache nil
 	 math-radix-float-cache-tag nil
-	 math-random-cache nil
+	 math-random-cache nil ; obsolescent as of 29.1
 	 math-max-digits-cache nil
 	 math-integral-cache nil
 	 math-units-table nil
-- 
2.34.1

From 573d09bfa3b472e31795add03ae6a58c7ad347d1 Mon Sep 17 00:00:00 2001
From: Paul Eggert <[email-masked]>
Date: Sun, 16 Oct 2022 23:37:07 -0700
Subject: [PATCH 04/10] =?UTF-8?q?Simplify=20calc-prog=20by=20using=20?=
 =?UTF-8?q?=E2=80=98random=E2=80=99?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* lisp/calc/calc-prog.el (calc-user-define-formula):
Simplify by using (random N).
---
 lisp/calc/calc-prog.el | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/lisp/calc/calc-prog.el b/lisp/calc/calc-prog.el
index 127f6340a1..0409ef8027 100644
--- a/lisp/calc/calc-prog.el
+++ b/lisp/calc/calc-prog.el
@@ -200,7 +200,7 @@ calc-user-define-formula
 			  (format "%03d" key)))
 	   odef (assq key (calc-user-key-map)))
      (unless keyname
-       (setq keyname (format "%05d" (abs (% (random) 10000)))))
+       (setq keyname (format "%05d" (random 10000))))
      (while
 	 (progn
 	   (setq cmd-base-default (concat "User-" keyname))
@@ -268,7 +268,7 @@ calc-user-define-formula
 	 (setq func (intern (concat "calcFunc-User"
 				    (or keyname
 					(and cmd (symbol-name cmd))
-					(format "%05d" (% (random) 10000)))))))
+					(format "%05d" (random 10000)))))))
 
      (if is-lambda
 	 (setq calc-user-formula-alist math-arglist)
-- 
2.34.1

From b111f941e13bd627870f7e812b8be15b7fee4548 Mon Sep 17 00:00:00 2001
From: Paul Eggert <[email-masked]>
Date: Sun, 16 Oct 2022 21:35:47 -0700
Subject: [PATCH 05/10] =?UTF-8?q?*=20doc/lispref/numbers.texi:=20Improve?=
 =?UTF-8?q?=20=E2=80=98random=E2=80=99=20doc.?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 doc/lispref/numbers.texi | 18 +++++++++++++-----
 1 file changed, 13 insertions(+), 5 deletions(-)

diff --git a/doc/lispref/numbers.texi b/doc/lispref/numbers.texi
index fdcda328d8..c1a1349d1a 100644
--- a/doc/lispref/numbers.texi
+++ b/doc/lispref/numbers.texi
@@ -1238,6 +1238,9 @@ Random Numbers
 sequence of numbers.  By default, Emacs initializes the random seed at
 startup, in such a way that the sequence of values of @code{random}
 (with overwhelming likelihood) differs in each Emacs run.
+The random seed is typically initialized from system entropy;
+however, on obsolescent platforms lacking entropy pools,
+the seed is taken from less-random volatile data such as the current time.
 
   Sometimes you want the random number sequence to be repeatable.  For
 example, when debugging a program whose behavior depends on the random
@@ -1256,12 +1259,17 @@ Random Numbers
 any fixnum, i.e., any integer from @code{most-negative-fixnum} through
 @code{most-positive-fixnum} (@pxref{Integer Basics}).
 
+If @var{limit} is a string, it means to choose a new seed based on the
+string's contents.  This causes later calls to @code{random} to return
+a reproducible sequence of results.
+
 If @var{limit} is @code{t}, it means to choose a new seed as if Emacs
-were restarting, typically from the system entropy.  On systems
-lacking entropy pools, choose the seed from less-random volatile data
-such as the current time.
+were restarting.  This causes later calls to @code{random} to return
+an unpredictable sequence of results.
 
-If @var{limit} is a string, it means to choose a new seed based on the
-string's contents.
+If you need a random nonce for cryptographic purposes, @code{(random
+t)} is typically not the best approach, as it can adversely affect other
+parts of your program that benefit from reproducible results, and it can
+leave information about the nonce scattered about Emacs's internal state.
 
 @end defun
-- 
2.34.1

From 7113ce5ab4a265db7f2870c6614da88d09407604 Mon Sep 17 00:00:00 2001
From: Paul Eggert <[email-masked]>
Date: Sun, 16 Oct 2022 16:33:05 -0700
Subject: [PATCH 06/10] New function make-nonce

* src/alloc.c (clear_nonce, Fmake_nonce): New functions.
* src/fns.c: Do not include <sys/random.h>.
(extract_data_from_object): Simplify by calling get_entropy.
* src/sysdep.c (get_entropy): New function, taken from
the old extract_data_from_object.
---
 doc/lispref/numbers.texi |  2 ++
 doc/lispref/strings.texi | 12 ++++++++++++
 etc/NEWS                 |  4 ++++
 src/alloc.c              | 36 ++++++++++++++++++++++++++++++++++++
 src/fns.c                | 12 +-----------
 src/lisp.h               |  3 ++-
 src/sysdep.c             | 16 ++++++++++++++++
 7 files changed, 73 insertions(+), 12 deletions(-)

diff --git a/doc/lispref/numbers.texi b/doc/lispref/numbers.texi
index c1a1349d1a..079f7bda9c 100644
--- a/doc/lispref/numbers.texi
+++ b/doc/lispref/numbers.texi
@@ -1271,5 +1271,7 @@ Random Numbers
 t)} is typically not the best approach, as it can adversely affect other
 parts of your program that benefit from reproducible results, and it can
 leave information about the nonce scattered about Emacs's internal state.
+For nonces, it is typically better to use @code{make-nonce}
+(@pxref{Creating Strings}).
 
 @end defun
diff --git a/doc/lispref/strings.texi b/doc/lispref/strings.texi
index cf961e9e7c..0f3e0ae213 100644
--- a/doc/lispref/strings.texi
+++ b/doc/lispref/strings.texi
@@ -455,6 +455,18 @@ Creating Strings
 Remove the final newline, if any, from @var{string}.
 @end defun
 
+@defun make-nonce length &optional function
+Return a newly created random string of length @var{length}.
+The string is unibyte, with bytes taken from system entropy,
+and with each string value equally likely.
+
+If @var{function} is given, call it with the newly created string as
+an argument and return the value that @var{function} returns.
+When the function exits, overwrite the string's random contents with
+unspecified bytes, to lessen information leakage in insecure code.
+The string's contents are therefore valid only during the function call.
+@end defun
+
 @node Modifying Strings
 @section Modifying Strings
 @cindex modifying strings
diff --git a/etc/NEWS b/etc/NEWS
index 041fe0bdbd..2938d6acaf 100644
--- a/etc/NEWS
+++ b/etc/NEWS
@@ -3422,6 +3422,10 @@ string, and a callback is called when the user types 'C-c C-c'.
 This is a modal version of 'string-edit', and can be used as an
 alternative to 'read-string'.
 
++++
+** New function 'make-nonce'.
+This creates a random string, useful for cryptographic nonces.
+
 +++
 ** The return value of 'clear-message-function' is not ignored anymore.
 If the function returns 'dont-clear-message', then the message is not
diff --git a/src/alloc.c b/src/alloc.c
index 419c5e558b..f8fd954ff6 100644
--- a/src/alloc.c
+++ b/src/alloc.c
@@ -2266,6 +2266,41 @@ DEFUN ("make-string", Fmake_string, Smake_string, 2, 3, 0,
   return val;
 }
 
+/* Set all the bytes of NONCE (a unibyte string) to unspecifed values,
+   as securely as possible.  */
+
+static void
+clear_nonce (Lisp_Object nonce)
+{
+  explicit_bzero (SDATA (nonce), SCHARS (nonce));
+}
+
+DEFUN ("make-nonce", Fmake_nonce, Smake_nonce,
+       1, 2, 0,
+       doc: /* Return a random string of length LENGTH.
+The string is unibyte, with bytes taken from system entropy.
+
+If FUNCTION is given, call it with with newly created string as an
+argument and return the value that FUNCTION returns.  When the
+function exits, overwrite the newly created string's contents with
+unspecified bytes, for security.  */)
+  (Lisp_Object length, Lisp_Object function)
+{
+  CHECK_FIXNAT (length);
+  EMACS_INT nbytes = XFIXNAT (length);
+  Lisp_Object nonce = make_uninit_string (nbytes);
+  get_entropy (SDATA (nonce), XFIXNAT (length));
+  if (NILP (function))
+    return nonce;
+
+  /* Pin the string, and clear it after FUNCTION exits, to lessen
+     information leakage if Emacs is buggy elsewhere.  */
+  pin_string (nonce);
+  specpdl_ref count = SPECPDL_INDEX ();
+  record_unwind_protect (clear_nonce, nonce);
+  return unbind_to (count, call1 (function, nonce));
+}
+
 /* Fill A with 1 bits if INIT is non-nil, and with 0 bits otherwise.
    Return A.  */
 
@@ -7862,6 +7897,7 @@ syms_of_alloc (void)
   defsubr (&Smake_vector);
   defsubr (&Smake_record);
   defsubr (&Smake_string);
+  defsubr (&Smake_nonce);
   defsubr (&Smake_bool_vector);
   defsubr (&Smake_symbol);
   defsubr (&Smake_marker);
diff --git a/src/fns.c b/src/fns.c
index 4055792382..3d2c8c88ab 100644
--- a/src/fns.c
+++ b/src/fns.c
@@ -21,7 +21,6 @@ Copyright (C) 1985-1987, 1993-1995, 1997-2022 Free Software Foundation,
 #include <config.h>
 
 #include <stdlib.h>
-#include <sys/random.h>
 #include <unistd.h>
 #include <filevercmp.h>
 #include <intprops.h>
@@ -5683,16 +5682,7 @@ extract_data_from_object (Lisp_Object spec,
         {
 	  EMACS_INT start_hold = XFIXNAT (start);
           object = make_uninit_string (start_hold);
-	  char *lim = SSDATA (object) + start_hold;
-	  for (char *p = SSDATA (object); p < lim; p++)
-	    {
-	      ssize_t gotten = getrandom (p, lim - p, 0);
-	      if (0 <= gotten)
-		p += gotten;
-	      else if (errno != EINTR)
-		report_file_error ("Getting random data", Qnil);
-	    }
-
+	  get_entropy (SDATA (object), start_hold);
           *start_byte = 0;
           *end_byte = start_hold;
         }
diff --git a/src/lisp.h b/src/lisp.h
index 5f6721595c..053725b798 100644
--- a/src/lisp.h
+++ b/src/lisp.h
@@ -1581,7 +1581,7 @@ CDR_SAFE (Lisp_Object c)
 	 If negative, the string is unibyte:
 	 -1 for data normally allocated
 	 -2 for data in rodata (C string constants)
-	 -3 for data that must be immovable (used for bytecode)  */
+	 -3 for data that must be immovable (used for bytecode and nonces)  */
       ptrdiff_t size_byte;
 
       INTERVAL intervals;	/* Text properties in this string.  */
@@ -5042,6 +5042,7 @@ maybe_disable_address_randomization (int argc, char **argv)
 extern unsigned long int get_random_ulong (void);
 extern void seed_random (void *, ptrdiff_t);
 extern void init_random (void);
+extern void get_entropy (void *, ptrdiff_t);
 extern void emacs_backtrace (int);
 extern AVOID emacs_abort (void) NO_INLINE;
 extern int emacs_fstatat (int, char const *, void *, int);
diff --git a/src/sysdep.c b/src/sysdep.c
index 4786c8fa4f..5117460fc0 100644
--- a/src/sysdep.c
+++ b/src/sysdep.c
@@ -2159,6 +2159,22 @@ seed_random (void *seed, ptrdiff_t seed_size)
   set_random_seed (arg);
 }
 
+/* Set BUF, of size BUFSIZE, to random data derived from system entropy.  */
+
+void
+get_entropy (void *buf, ptrdiff_t bufsize)
+{
+  char *p = buf, *lim = p + bufsize;
+  while (p < lim)
+    {
+      ssize_t gotten = getrandom (p, lim - p, 0);
+      if (0 <= gotten)
+	p += gotten;
+      else if (errno != EINTR)
+	report_file_error ("Getting random data", Qnil);
+    }
+}
+
 void
 init_random (void)
 {
-- 
2.34.1

From 45efe70ce2fff4211013b688911b76a6400fb0f5 Mon Sep 17 00:00:00 2001
From: Paul Eggert <[email-masked]>
Date: Mon, 17 Oct 2022 00:05:31 -0700
Subject: [PATCH 07/10] =?UTF-8?q?Simplify=20auth-source--obfuscate=20via?=
 =?UTF-8?q?=20=E2=80=98make-nonce=E2=80=99?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* lisp/auth-source.el (auth-source--obfuscate):
Simplify by using make-nonce.  This also improves the nonce by using
all the bits in each byte.
---
 lisp/auth-source.el | 4 +---
 1 file changed, 1 insertion(+), 3 deletions(-)

diff --git a/lisp/auth-source.el b/lisp/auth-source.el
index feefd391a8..cba7324ea9 100644
--- a/lisp/auth-source.el
+++ b/lisp/auth-source.el
@@ -1216,9 +1216,7 @@ auth-source--obfuscate
   ;; useful information is leaked.  If you reset the nonce, you also
   ;; have to call `auth-source-forget-all-cached'.
   (unless auth-source--session-nonce
-    (setq auth-source--session-nonce
-          (apply #'string (cl-loop repeat 16
-                                   collect (random 128)))))
+    (setq auth-source--session-nonce (make-nonce 16)))
   (if (and (fboundp 'gnutls-symmetric-encrypt)
            (gnutls-available-p))
       (let ((cdata (car (last (gnutls-ciphers)))))
-- 
2.34.1

From e5066b0b790f3d809b016967b222c4eafe8e7b88 Mon Sep 17 00:00:00 2001
From: Paul Eggert <[email-masked]>
Date: Sun, 16 Oct 2022 23:37:58 -0700
Subject: [PATCH 08/10] Simplify ntml by using make-nonce

* lisp/net/ntlm.el (ntlm-generate-nonce): Simplify.
---
 lisp/net/ntlm.el | 4 +---
 1 file changed, 1 insertion(+), 3 deletions(-)

diff --git a/lisp/net/ntlm.el b/lisp/net/ntlm.el
index b58f0abb56..03a6c37ff3 100644
--- a/lisp/net/ntlm.el
+++ b/lisp/net/ntlm.el
@@ -221,9 +221,7 @@ ntlm-compute-timestamp
 (defun ntlm-generate-nonce ()
   "Generate a random nonce, not to be used more than once.
 Return a random eight byte unibyte string."
-  (unibyte-string
-   (random 256) (random 256) (random 256) (random 256)
-   (random 256) (random 256) (random 256) (random 256)))
+  (make-nonce 8))
 
 (defun ntlm-build-auth-response (challenge user password-hashes)
   "Return the response string to a challenge string CHALLENGE given by
-- 
2.34.1

From 124636f5202a41c8feb46330f3c8fcd9f4eed9e8 Mon Sep 17 00:00:00 2001
From: Paul Eggert <[email-masked]>
Date: Sun, 16 Oct 2022 23:40:14 -0700
Subject: [PATCH 09/10] Improve randomness of server.el

* lisp/server.el (server-generate-key): Generate from system
entropy, rather than from (random) which is lower quality.
---
 lisp/server.el | 23 +++++++++++++++++++----
 1 file changed, 19 insertions(+), 4 deletions(-)

diff --git a/lisp/server.el b/lisp/server.el
index 90d97c1538..b2bc245c02 100644
--- a/lisp/server.el
+++ b/lisp/server.el
@@ -590,10 +590,25 @@ server-generate-key
 The key is a 64-byte string of random chars in the range `!'..`~'.
 If called interactively, also inserts it into current buffer."
   (interactive)
-  (let ((auth-key
-	 (cl-loop repeat 64
-                  collect (+ 33 (random 94)) into auth
-                  finally return (concat auth))))
+  (let* ((base 94) (auth-key-len 64)
+	 (auth-key (make-string auth-key-len 0))
+	 ;; 1+ because we divide by BASE first, before taking the remainder.
+	 ;; The division is first because if we took the remainder first
+	 ;; the first remainder would not be entirely random.
+	 (nonce-length (1+ (ceiling (logb (expt base auth-key-len)) 8))))
+    ;; Use make-nonce with a function arg, to clear the nonce.
+    ;; auth-key and the bignum n still have the nonce's info, though.
+    (make-nonce
+     nonce-length
+     #'(lambda (nonce)
+	 (let ((n 0))
+	   ;; Set N to be all the nonce's bits, concatenated.
+	   (cl-loop for i below nonce-length
+		    do (setq n (+ (* 256 n) (aref nonce i))))
+	   ;; Repeatedly divide and remainder to compute each byte.
+	   (cl-loop for i below auth-key-len
+		    do (setq n (/ n base))
+		       (aset auth-key i (+ 33 (% n base)))))))
     (if (called-interactively-p 'interactive)
 	(insert auth-key))
     auth-key))
-- 
2.34.1

From 54a2d5a65b2e868ce3b18c47f50886d07414adec Mon Sep 17 00:00:00 2001
From: Paul Eggert <[email-masked]>
Date: Mon, 17 Oct 2022 00:00:44 -0700
Subject: [PATCH 10/10] Improve randomness of message-canlock-generate

* lisp/gnus/message.el (message-canlock-generate):
Simplify by using make-nonce.
---
 lisp/gnus/message.el | 5 +----
 1 file changed, 1 insertion(+), 4 deletions(-)

diff --git a/lisp/gnus/message.el b/lisp/gnus/message.el
index 5e4e9854a6..670844703d 100644
--- a/lisp/gnus/message.el
+++ b/lisp/gnus/message.el
@@ -5192,10 +5192,7 @@ message-send-mail-with-mailclient
 (defun message-canlock-generate ()
   "Return a string that is non-trivial to guess.
 Do not use this for anything important, it is cryptographically weak."
-  (sha1 (concat (message-unique-id)
-                (format "%x%x%x" (random) (random) (random))
-                (prin1-to-string (recent-keys))
-                (prin1-to-string (garbage-collect)))))
+  (make-nonce 20 #'sha1))
 
 (defvar canlock-password)
 (defvar canlock-password-for-verify)
-- 
2.34.1