zsh-workers
 help / color / mirror / code / Atom feed
* preliminary patch for zsh/random module
@ 2022-10-23  3:43 Clinton Bunch
  2022-10-24  2:01 ` Bart Schaefer
  0 siblings, 1 reply; 6+ messages in thread
From: Clinton Bunch @ 2022-10-23  3:43 UTC (permalink / raw)
  To: zsh-workers

It still needs testing on more platforms and additional error-checking 
as well as documentation, but I was hoping for some feedback on the 
general ideas.

diff --git a/Src/Modules/random.c b/Src/Modules/random.c
new file mode 100644
index 000000000..038a56c1f
--- /dev/null
+++ b/Src/Modules/random.c
@@ -0,0 +1,350 @@
+/*
+ * random.c - module to access kernel randome sources.
+ *
+ * This file is part of zsh, the Z shell.
+ *
+ * Copyright (c) 2022 Clinton Bunch
+ * All rights reserved.
+ *
+ * Permission is hereby granted, without written agreement and without
+ * license or royalty fees, to use, copy, modify, and distribute this
+ * software and to distribute modified versions of this software for any
+ * purpose, provided that the above copyright notice and the following
+ * two paragraphs appear in all copies of this software.
+ *
+ * In no event shall Zoltán Hidvégi or the Zsh Development Group be liable
+ * to any party for direct, indirect, special, incidental, or consequential
+ * damages arising out of the use of this software and its documentation,
+ * even if Zoltán Hidvégi and the Zsh Development Group have been 
advised of
+ * the possibility of such damage.
+ *
+ * Clinton Bunch and the Zsh Development Group specifically disclaim any
+ * warranties, including, but not limited to, the implied warranties of
+ * merchantability and fitness for a particular purpose.  The software
+ * provided hereunder is on an "as is" basis, and Zoltán Hidvégi and the
+ * Zsh Development Group have no obligation to provide maintenance,
+ * support, updates, enhancements, or modifications.
+ *
+ */
+
+#include "random.mdh"
+#include "random.pro"
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <stdio.h>
+
+#ifdef HAVE_SYS_RANDOM_H
+#include <sys/random.h>
+#endif
+
+/* Simplify select URANDOM specific code */
+#if !defined(HAVE_ARC4RANDOM) && !defined(HAVE_GETRANDOM)
+#define USE_URANDOM
+#endif
+
+/* Define a buffer type for 32 bit integers */
+#ifdef UINT32_MAX
+typedef uint32_t zuint32;
+#else
+#define UINT32_MAX  UINT_MAX
+typedef unsigned int zuint32;
+#endif
+
+/* buffer to pre-load integers for SRANDOM to lessen the context 
switches */
+zuint32 rand_buff[8];
+static int buf_cnt = -1;
+
+#ifdef USE_URANDOM
+/* File descriptor for /dev/urandom */
+int randfd = -1;
+#endif /* USE_URANDOM */
+
+/*
+ *  Function takes an input buffer and converts the characters to hex and
+ *  places them in an outbut buffer twice the size. Returns -1 if it can't.
+ *  Returns the size of the output otherwise.
+ */
+
+/**/
+static int
+ztr_to_hex(char *out, size_t outlen, unsigned char *input, size_t inlen)
+{
+    int i = 0, j = 0;
+
+    if (outlen < (inlen * 2 + 1))
+    return -1;
+
+    for(i = 0, j = 0; i < inlen; i++, j+=2) {
+    if (j < outlen) {
+        sprintf((char *)(out + j),"%02X",input[i]);
+    } else {
+        return -1;
+    }
+    }
+    out[j]='\0';
+    return j;
+}
+
+/**/
+static int
+getrandom_buffer(void *buf, size_t len)
+{
+    int ret;
+
+#ifdef HAVE_ARC4RANDOM
+    arc4random_buf(buf,len);
+    ret = len;
+#elif defined(HAVE_GETRANDOM)
+    ret=getrandom(buf,len,0);
+#else
+    ret=read(randfd,buf,len);
+#endif
+    return ret;
+}
+
+/*
+ * Implements the getrandom builtin to return a string of random bytes of
+ * user-specified length.  It either prints them to stdout or returns them
+ * in a parameter passed on the command line.
+ *
+ */
+
+/**/
+static int
+bin_getrandom(char *name, char **argv, Options ops, int func)
+{
+
+    size_t len=8;
+    size_t byte_len=0, outlen;
+    int integer_out=0, i;
+    char int2str[11];  /*maximum string length of a 32-bit integer + 
null */
+
+    unsigned char *buf;
+    zuint32  *int_buf;
+    char *scalar = NULL, *arrname= NULL, *endptr, *len_arg, *outbuff;
+    char **array;
+
+    if (OPT_ISSET(ops, 's')) {
+    scalar = OPT_ARG(ops, 's');
+    if (!isident(scalar)) {
+        zwarnnam(name, "argument to -s not an identifier: %s", scalar);
+        return 1;
+    }
+    }
+
+    if (OPT_ISSET(ops,'a')) {
+    arrname = OPT_ARG(ops, 'a');
+    if (!isident(arrname)) {
+        zwarnnam(name,"argument to -a not an identifier: %s", arrname);
+        return 1;
+    }
+    }
+
+    if (OPT_ISSET(ops, 'i')) {
+    if (!OPT_ISSET(ops,'a')) {
+        zwarnnam(name,"-i requires -a");
+        return 1;
+    }
+    integer_out=1;
+    }
+
+    if (OPT_ISSET(ops, 'l')) {
+    errno = 0;
+
+    len_arg=OPT_ARG(ops, 'l');
+    len = strtoul(len_arg, &endptr, 10);
+    if (errno != 0) {
+        zwarnnam(name, "%s: %e", len_arg, errno);
+        return 1;
+    } else if (*len_arg == '\0' || *endptr != '\0') {
+        zwarnnam(name, "%s: invalid decimal number", len_arg);
+        return 1;
+    } else if (len > 64 || (integer_out && len > 16) || len <= 0) {
+        zwarnnam(name,"length must be between 1 and %d you specified: %d",
+            (integer_out?16:64), len);
+        return 1;
+    }
+
+    }
+
+    if (!byte_len)
+    byte_len = len;
+    if (integer_out)
+    byte_len = len*sizeof(zuint32);
+
+    buf = zalloc(byte_len);
+
+    if (getrandom_buffer(buf, byte_len) < 0) {
+    zwarnnam(name,"Couldn't get random data");
+    return 1;
+    }
+
+    if (OPT_ISSET(ops, 'r')) {
+    outbuff = (char *) buf;
+    outlen  = byte_len;
+    } else {
+    outlen=byte_len*2+1;
+    outbuff = (char*) zalloc(outlen);
+    ztr_to_hex(outbuff, outlen, buf, byte_len);
+    }
+
+    if (scalar) {
+    setsparam(scalar, metafy(outbuff, outlen, META_DUP));
+    }
+
+    if(arrname) {
+    array = (char **) zalloc((len+1)*sizeof(char *));
+    array[len] = NULL;
+
+    if(integer_out)
+        int_buf=(zuint32 *)buf;
+
+    for(i=0;i < len;i++) {
+        if(integer_out) {
+        sprintf(int2str,"%u",int_buf[i]);
+        } else {
+        sprintf(int2str,"%d",buf[i]);
+        }
+        array[i]=ztrdup(int2str);
+    }
+    setaparam(arrname,array);
+    }
+
+    if (!scalar && !arrname) {
+        fwrite(outbuff,1,outlen,stdout);
+        if (outbuff !=  (void *) buf) {
+        zfree(outbuff,outlen);
+    }
+    }
+
+
+    zfree(buf, len);
+    return 0;
+}
+
+
+
+/**/
+static zlong
+get_srandom(UNUSED(Param pm)) {
+
+    if(buf_cnt < 0) {
+    getrandom_buffer((void*) rand_buff,sizeof(rand_buff));
+    buf_cnt=7;
+    }
+    return rand_buff[buf_cnt--];
+}
+
+/**/
+static mnumber
+math_zrandom(UNUSED(char *name), UNUSED(int argc), UNUSED(mnumber *argv),
+         UNUSED(int id))
+{
+    mnumber ret;
+    uint32_t r;
+
+    r=get_srandom(NULL);
+    ret.type = MN_FLOAT;
+    ret.u.d = r/(UINT32_MAX+0.0);
+
+    return ret;
+}
+
+static struct builtin bintab[] = {
+    BUILTIN("getrandom", 0, bin_getrandom, 0, 5, 0, "ria:s:l:%", NULL),
+};
+
+static const struct gsu_integer srandom_gsu =
+{ get_srandom, nullintsetfn, stdunsetfn };
+
+static struct paramdef patab[] = {
+    SPECIALPMDEF("SRANDOM", PM_INTEGER|PM_READONLY, 
&srandom_gsu,NULL,NULL),
+};
+
+static struct mathfunc mftab[] = {
+    NUMMATHFUNC("zrandom", math_zrandom, 0, 0, 0),
+};
+
+static struct features module_features = {
+    bintab, sizeof(bintab)/sizeof(*bintab),
+    NULL, 0,
+    mftab, sizeof(mftab)/sizeof(*mftab),
+    patab, sizeof(patab)/sizeof(*patab),
+    0
+};
+
+/**/
+int
+setup_(UNUSED(Module m))
+{
+#ifdef USE_URANDOM
+    struct stat st;
+
+    if (lstat("/dev/urandom",&st) < 0) {
+    zwarn("No kernel random pool found.");
+    return 1;
+    }
+
+    if (!(S_ISCHR(st.st_mode)) ) {
+    zwarn("No kernel random pool found.");
+    return 1;
+    }
+#endif /* USE_URANDOM */
+    return 0;
+}
+
+/**/
+int
+features_(Module m, char ***features)
+{
+    *features = featuresarray(m, &module_features);
+    return 0;
+}
+
+/**/
+int
+enables_(Module m, int **enables)
+{
+    return handlefeatures(m, &module_features, enables);
+}
+
+/**/
+int
+boot_(Module m)
+{
+#ifdef USE_URANDOM
+    int tmpfd=-1;
+
+    if ((tmpfd = open("/dev/urandom", O_RDONLY)) < 0) {
+    zwarnnam("Could not access kernel random pool");
+    return 1;
+    }
+    randfd = movefd(tmpfd);
+    if (randfd < 0)
+    zwarnnam("Could not access kernel random pool");
+    return 1;
+    }
+#endif
+    return 0;
+}
+
+/**/
+int
+cleanup_(Module m)
+{
+    return setfeatureenables(m, &module_features, NULL);
+}
+
+/**/
+int
+finish_(UNUSED(Module m))
+{
+#ifdef USE_URANDOM
+    if (randfd >= 0)
+    zclose(randfd);
+#endif /* USE_URANDOM */
+    return 0;
+}
diff --git a/Src/Modules/random.mdd b/Src/Modules/random.mdd
new file mode 100644
index 000000000..3803a4533
--- /dev/null
+++ b/Src/Modules/random.mdd
@@ -0,0 +1,7 @@
+name=zsh/random
+link=either
+load=yes
+
+autofeatures="b:getrandom p:SRANDOM f:zrandom"
+
+objects="random.o"
diff --git a/configure.ac b/configure.ac
index 074141d38..f1fa01274 100644
--- a/configure.ac
+++ b/configure.ac
@@ -675,6 +675,7 @@ fi
  AC_CHECK_HEADERS(sys/time.h sys/times.h sys/select.h termcap.h termio.h \
           termios.h sys/param.h sys/filio.h string.h memory.h \
           limits.h fcntl.h libc.h sys/utsname.h sys/resource.h \
+                 sys/random.h \
           locale.h errno.h stdio.h stdarg.h varargs.h stdlib.h \
           unistd.h sys/capability.h \
           utmp.h utmpx.h sys/types.h pwd.h grp.h poll.h sys/mman.h \
@@ -1337,6 +1338,7 @@ AC_CHECK_FUNCS(strftime strptime mktime timelocal \
             cygwin_conv_path \
             nanosleep \
             srand_deterministic \
+               getrandom arc4random \
             setutxent getutxent endutxent getutent)
  AC_FUNC_STRCOLL




^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: preliminary patch for zsh/random module
  2022-10-23  3:43 preliminary patch for zsh/random module Clinton Bunch
@ 2022-10-24  2:01 ` Bart Schaefer
  2022-10-24  2:56   ` Clinton Bunch
  0 siblings, 1 reply; 6+ messages in thread
From: Bart Schaefer @ 2022-10-24  2:01 UTC (permalink / raw)
  To: Clinton Bunch; +Cc: zsh-workers

On Sat, Oct 22, 2022 at 8:44 PM Clinton Bunch <cdb_zsh@zentaur.org> wrote:
>
> It still needs testing on more platforms and additional error-checking
> as well as documentation, but I was hoping for some feedback on the
> general ideas.

Silly nits:
* Typo in the introductory comment.
* Indentation is funny, it looks like you may have used tab width
other than 8 characters, which is the standard for zsh C code.  (Or
just never use tabs to indent.)
* There should be spaces on either side of assignment "=" signs.  You
haven't been consistent.

Aside to -workers:  It's probably time to remove Zoltan's name from
the copyright notices ... or more likely, to specify that the authors
of new files should replace his name with their own name there.

Actual question:  What's the use case for returning or printing a
block of random bytes?  Why does this need to be a builtin?

Suggestion:  Treat SRANDOM like SECONDS, in that you can change the
type from integer to floating-point.  Then maybe the zrandom() math
function isn't needed?


^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: preliminary patch for zsh/random module
  2022-10-24  2:01 ` Bart Schaefer
@ 2022-10-24  2:56   ` Clinton Bunch
  2022-10-24  4:47     ` Bart Schaefer
  0 siblings, 1 reply; 6+ messages in thread
From: Clinton Bunch @ 2022-10-24  2:56 UTC (permalink / raw)
  To: zsh-workers

On 10/23/2022 9:01 PM, Bart Schaefer wrote:
> On Sat, Oct 22, 2022 at 8:44 PM Clinton Bunch <cdb_zsh@zentaur.org> wrote:
>> It still needs testing on more platforms and additional error-checking
>> as well as documentation, but I was hoping for some feedback on the
>> general ideas.
> Silly nits:
> * Typo in the introductory comment.
I'm pretty sure I've since found and changed all the instances of 
Zoltan's name.
> * Indentation is funny, it looks like you may have used tab width
> other than 8 characters, which is the standard for zsh C code.  (Or
> just never use tabs to indent.)
Blame notepad.  Thunderbird doesn't allow me to attach inline, so I had 
to open in Notepad and cut-n-paste.  I didn't notice until after I sent 
it, how bad the indentation got messed up.
> * There should be spaces on either side of assignment "=" signs.  You
> haven't been consistent.
I think I fixed all those.  I haven't been consistent because it seems 
unnatural to me.  Waste of line width.
>
> Aside to -workers:  It's probably time to remove Zoltan's name from
> the copyright notices ... or more likely, to specify that the authors
> of new files should replace his name with their own name there.
>
> Actual question:  What's the use case for returning or printing a
> block of random bytes?  Why does this need to be a builtin?
Mostly because I see constructs like read -k6 -u3 3</dev/urandom, also 
it would be nice to initialize an array with random numbers without 
having to use a loop to access SRANDOM n times.
>
> Suggestion:  Treat SRANDOM like SECONDS, in that you can change the
> type from integer to floating-point.  Then maybe the zrandom() math
> function isn't needed?
That would seem confusing to me, and too easy to forget which state you 
left it in. zrandom was meant to be a replacement for rand48
>



^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: preliminary patch for zsh/random module
  2022-10-24  2:56   ` Clinton Bunch
@ 2022-10-24  4:47     ` Bart Schaefer
  2022-10-24 13:20       ` Clinton Bunch
  0 siblings, 1 reply; 6+ messages in thread
From: Bart Schaefer @ 2022-10-24  4:47 UTC (permalink / raw)
  To: Clinton Bunch; +Cc: zsh-workers

On Sun, Oct 23, 2022 at 7:57 PM Clinton Bunch <cdb_zsh@zentaur.org> wrote:
>
> On 10/23/2022 9:01 PM, Bart Schaefer wrote:
> > * Typo in the introductory comment.
> I'm pretty sure I've since found and changed all the instances of
> Zoltan's name.

I was referring to the spelling of "randome".

> > Actual question:  What's the use case for returning or printing a
> > block of random bytes?  Why does this need to be a builtin?
> Mostly because I see constructs like read -k6 -u3 3</dev/urandom

... because?

> it would be nice to initialize an array with random numbers without
> having to use a loop to access SRANDOM n times.

But you're not filling an array with random numbers, you're filling a
string (scalar) with random bytes.

> > Suggestion:  Treat SRANDOM like SECONDS, in that you can change the
> > type from integer to floating-point.  Then maybe the zrandom() math
> > function isn't needed?
> That would seem confusing to me, and too easy to forget which state you
> left it in.

You make it local so you're not leaving it.

() {
 print $SECONDS;
 () {
  local -F SECONDS
  print $SECONDS
 }
 print $SECONDS
}
56
0.0000050000
56

> zrandom was meant to be a replacement for rand48

OK.


^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: preliminary patch for zsh/random module
  2022-10-24  4:47     ` Bart Schaefer
@ 2022-10-24 13:20       ` Clinton Bunch
  2022-10-24 21:15         ` Clinton Bunch
  0 siblings, 1 reply; 6+ messages in thread
From: Clinton Bunch @ 2022-10-24 13:20 UTC (permalink / raw)
  To: zsh-workers

On 10/23/2022 11:47 PM, Bart Schaefer wrote:
> On Sun, Oct 23, 2022 at 7:57 PM Clinton Bunch <cdb_zsh@zentaur.org> wrote:
>> On 10/23/2022 9:01 PM, Bart Schaefer wrote:
>>> * Typo in the introductory comment.
>> I'm pretty sure I've since found and changed all the instances of
>> Zoltan's name.
> I was referring to the spelling of "randome".
>
>>> Actual question:  What's the use case for returning or printing a
>>> block of random bytes?  Why does this need to be a builtin?
>> Mostly because I see constructs like read -k6 -u3 3</dev/urandom
> ... because?

I did it to seed rand48.  Roman did it to generate a 32-bit random 
integer.  Both of which are obsoleted by other parts of this module.

I'm sure there are other reasons someone might want to read more or less 
than 4 bytes of random data.  One that comes to mind is generating a 
password in a platform-independent way without assuming perl or python.

>
>> it would be nice to initialize an array with random numbers without
>> having to use a loop to access SRANDOM n times.
> But you're not filling an array with random numbers, you're filling a
> string (scalar) with random bytes.
Actually, it does both.
>
>>> Suggestion:  Treat SRANDOM like SECONDS, in that you can change the
>>> type from integer to floating-point.  Then maybe the zrandom() math
>>> function isn't needed?
>> That would seem confusing to me, and too easy to forget which state you
>> left it in.
> You make it local so you're not leaving it.
>
> () {
>   print $SECONDS;
>   () {
>    local -F SECONDS
>    print $SECONDS
>   }
>   print $SECONDS
> }
> 56
> 0.0000050000
> 56
Still seems like something you do because you can rather than because 
it's intuitive to the user.  Also, your example assumes you'd only want 
to do this in a function (or wrap it in an anonymous function just for 
this purpose)
>
>> zrandom was meant to be a replacement for rand48
> OK.
>



^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: preliminary patch for zsh/random module
  2022-10-24 13:20       ` Clinton Bunch
@ 2022-10-24 21:15         ` Clinton Bunch
  0 siblings, 0 replies; 6+ messages in thread
From: Clinton Bunch @ 2022-10-24 21:15 UTC (permalink / raw)
  To: zsh-workers

[-- Attachment #1: Type: text/plain, Size: 2567 bytes --]


On 10/24/2022 8:20 AM, Clinton Bunch wrote:
> On 10/23/2022 11:47 PM, Bart Schaefer wrote:
>> On Sun, Oct 23, 2022 at 7:57 PM Clinton Bunch <cdb_zsh@zentaur.org> 
>> wrote:
>>> On 10/23/2022 9:01 PM, Bart Schaefer wrote:
>>>> * Typo in the introductory comment.
>>> I'm pretty sure I've since found and changed all the instances of
>>> Zoltan's name.
>> I was referring to the spelling of "randome".
>>
>>>> Actual question:  What's the use case for returning or printing a
>>>> block of random bytes?  Why does this need to be a builtin?
>>> Mostly because I see constructs like read -k6 -u3 3</dev/urandom
>> ... because?
>
> I did it to seed rand48.  Roman did it to generate a 32-bit random 
> integer.  Both of which are obsoleted by other parts of this module.
>
> I'm sure there are other reasons someone might want to read more or 
> less than 4 bytes of random data.  One that comes to mind is 
> generating a password in a platform-independent way without assuming 
> perl or python.
>
Here's an example:

randpw () {
         local 
chars="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz01234567890@#%&()"
         local tmp
         local -i num
         num=${1:-10}
         getrandom -l $num -s tmp
         for i in {1..${#tmp}..2}
         do
                 print -n ${chars["16#${tmp[$i,$i+1]}"%${#chars}+1]}
         done
         echo
}

>>
>>> it would be nice to initialize an array with random numbers without
>>> having to use a loop to access SRANDOM n times.
>> But you're not filling an array with random numbers, you're filling a
>> string (scalar) with random bytes.
> Actually, it does both.
>>
>>>> Suggestion:  Treat SRANDOM like SECONDS, in that you can change the
>>>> type from integer to floating-point.  Then maybe the zrandom() math
>>>> function isn't needed?
>>> That would seem confusing to me, and too easy to forget which state you
>>> left it in.
>> You make it local so you're not leaving it.
>>
>> () {
>>   print $SECONDS;
>>   () {
>>    local -F SECONDS
>>    print $SECONDS
>>   }
>>   print $SECONDS
>> }
>> 56
>> 0.0000050000
>> 56
> Still seems like something you do because you can rather than because 
> it's intuitive to the user.  Also, your example assumes you'd only 
> want to do this in a function (or wrap it in an anonymous function 
> just for this purpose)
>>
>>> zrandom was meant to be a replacement for rand48
>> OK.
>>
>
>

[-- Attachment #2: Type: text/html, Size: 4928 bytes --]

^ permalink raw reply	[flat|nested] 6+ messages in thread

end of thread, other threads:[~2022-10-24 21:16 UTC | newest]

Thread overview: 6+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2022-10-23  3:43 preliminary patch for zsh/random module Clinton Bunch
2022-10-24  2:01 ` Bart Schaefer
2022-10-24  2:56   ` Clinton Bunch
2022-10-24  4:47     ` Bart Schaefer
2022-10-24 13:20       ` Clinton Bunch
2022-10-24 21:15         ` Clinton Bunch

Code repositories for project(s) associated with this public inbox

	https://git.vuxu.org/mirror/zsh/

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).