From 7113ce5ab4a265db7f2870c6614da88d09407604 Mon Sep 17 00:00:00 2001 From: Paul Eggert 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 . (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 #include -#include #include #include #include @@ -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