Put sys-specific files in build.info
[openssl.git] / crypto / rand / rand_vms.c
index 9c462dd374911fdd6ec2b2f7c92585fd9667f492..d798f55e495cff234d3d1902b7c9e5b3c0a59e65 100644 (file)
 /*
- * Copyright 2001-2016 The OpenSSL Project Authors. All Rights Reserved.
+ * Copyright 2001-2018 The OpenSSL Project Authors. All Rights Reserved.
  *
- * Licensed under the OpenSSL license (the "License").  You may not use
+ * Licensed under the Apache License 2.0 (the "License").  You may not use
  * this file except in compliance with the License.  You can obtain a copy
  * in the file LICENSE in the source distribution or at
  * https://www.openssl.org/source/license.html
  */
 
-/*
- * Modified by VMS Software, Inc (2016)
- *    Eliminate looping through all processes (performance)
- *    Add additional randomizations using rand() function
- */
+#include "e_os.h"
 
+#define __NEW_STARLET 1         /* New starlet definitions since VMS 7.0 */
+#include <unistd.h>
+#include "internal/cryptlib.h"
 #include <openssl/rand.h>
-#include "rand_lcl.h"
-
-#if defined(OPENSSL_SYS_VMS)
-# include <descrip.h>
-# include <jpidef.h>
-# include <ssdef.h>
-# include <starlet.h>
-# include <efndef>
-# ifdef __DECC
-#  pragma message disable DOLLARID
-# endif
+#include "crypto/rand.h"
+#include "rand_local.h"
+#include <descrip.h>
+#include <dvidef.h>
+#include <jpidef.h>
+#include <rmidef.h>
+#include <syidef.h>
+#include <ssdef.h>
+#include <starlet.h>
+#include <efndef.h>
+#include <gen64def.h>
+#include <iosbdef.h>
+#include <iledef.h>
+#include <lib$routines.h>
+#ifdef __DECC
+# pragma message disable DOLLARID
+#endif
+
+#include <dlfcn.h>              /* SYS$GET_ENTROPY presence */
+
+#ifndef OPENSSL_RAND_SEED_OS
+# error "Unsupported seeding method configured; must be os"
+#endif
 
 /*
- * Use 32-bit pointers almost everywhere.  Define the type to which to cast a
- * pointer passed to an external function.
+ * DATA COLLECTION METHOD
+ * ======================
+ *
+ * This is a method to get low quality entropy.
+ * It works by collecting all kinds of statistical data that
+ * VMS offers and using them as random seed.
  */
-# if __INITIAL_POINTER_SIZE == 64
-#  define PTR_T __void_ptr64
-#  pragma pointer_size save
-#  pragma pointer_size 32
-# else                          /* __INITIAL_POINTER_SIZE == 64 */
-#  define PTR_T void *
-# endif                         /* __INITIAL_POINTER_SIZE == 64 [else] */
-
-static struct items_data_st {
+
+/* We need to make sure we have the right size pointer in some cases */
+#if __INITIAL_POINTER_SIZE == 64
+# pragma pointer_size save
+# pragma pointer_size 32
+#endif
+typedef uint32_t *uint32_t__ptr32;
+#if __INITIAL_POINTER_SIZE == 64
+# pragma pointer_size restore
+#endif
+
+struct item_st {
     short length, code;         /* length is number of bytes */
-} items_data[] = {
-    {4, JPI$_BUFIO},
-    {4, JPI$_CPUTIM},
-    {4, JPI$_DIRIO},
-    {4, JPI$_IMAGECOUNT},
-    {8, JPI$_LAST_LOGIN_I},
-    {8, JPI$_LOGINTIM},
-    {4, JPI$_PAGEFLTS},
-    {4, JPI$_PID},
-    {4, JPI$_PPGCNT},
-    {4, JPI$_WSPEAK},
-    {4, JPI$_FINALEXC},
-    {0, 0}                      /* zero terminated */
 };
 
-int RAND_poll(void)
+static const struct item_st DVI_item_data[] = {
+    {4,   DVI$_ERRCNT},
+    {4,   DVI$_REFCNT},
+};
+
+static const struct item_st JPI_item_data[] = {
+    {4,   JPI$_BUFIO},
+    {4,   JPI$_CPUTIM},
+    {4,   JPI$_DIRIO},
+    {4,   JPI$_IMAGECOUNT},
+    {4,   JPI$_PAGEFLTS},
+    {4,   JPI$_PID},
+    {4,   JPI$_PPGCNT},
+    {4,   JPI$_WSPEAK},
+    /*
+     * Note: the direct result is just a 32-bit address.  However, it points
+     * to a list of 4 32-bit words, so we make extra space for them so we can
+     * do in-place replacement of values
+     */
+    {16,  JPI$_FINALEXC},
+};
+
+static const struct item_st JPI_item_data_64bit[] = {
+    {8,   JPI$_LAST_LOGIN_I},
+    {8,   JPI$_LOGINTIM},
+};
+
+static const struct item_st RMI_item_data[] = {
+    {4,   RMI$_COLPG},
+    {4,   RMI$_MWAIT},
+    {4,   RMI$_CEF},
+    {4,   RMI$_PFW},
+    {4,   RMI$_LEF},
+    {4,   RMI$_LEFO},
+    {4,   RMI$_HIB},
+    {4,   RMI$_HIBO},
+    {4,   RMI$_SUSP},
+    {4,   RMI$_SUSPO},
+    {4,   RMI$_FPG},
+    {4,   RMI$_COM},
+    {4,   RMI$_COMO},
+    {4,   RMI$_CUR},
+#if defined __alpha
+    {4,   RMI$_FRLIST},
+    {4,   RMI$_MODLIST},
+#endif
+    {4,   RMI$_FAULTS},
+    {4,   RMI$_PREADS},
+    {4,   RMI$_PWRITES},
+    {4,   RMI$_PWRITIO},
+    {4,   RMI$_PREADIO},
+    {4,   RMI$_GVALFLTS},
+    {4,   RMI$_WRTINPROG},
+    {4,   RMI$_FREFLTS},
+    {4,   RMI$_DZROFLTS},
+    {4,   RMI$_SYSFAULTS},
+    {4,   RMI$_ISWPCNT},
+    {4,   RMI$_DIRIO},
+    {4,   RMI$_BUFIO},
+    {4,   RMI$_MBREADS},
+    {4,   RMI$_MBWRITES},
+    {4,   RMI$_LOGNAM},
+    {4,   RMI$_FCPCALLS},
+    {4,   RMI$_FCPREAD},
+    {4,   RMI$_FCPWRITE},
+    {4,   RMI$_FCPCACHE},
+    {4,   RMI$_FCPCPU},
+    {4,   RMI$_FCPHIT},
+    {4,   RMI$_FCPSPLIT},
+    {4,   RMI$_FCPFAULT},
+    {4,   RMI$_ENQNEW},
+    {4,   RMI$_ENQCVT},
+    {4,   RMI$_DEQ},
+    {4,   RMI$_BLKAST},
+    {4,   RMI$_ENQWAIT},
+    {4,   RMI$_ENQNOTQD},
+    {4,   RMI$_DLCKSRCH},
+    {4,   RMI$_DLCKFND},
+    {4,   RMI$_NUMLOCKS},
+    {4,   RMI$_NUMRES},
+    {4,   RMI$_ARRLOCPK},
+    {4,   RMI$_DEPLOCPK},
+    {4,   RMI$_ARRTRAPK},
+    {4,   RMI$_TRCNGLOS},
+    {4,   RMI$_RCVBUFFL},
+    {4,   RMI$_ENQNEWLOC},
+    {4,   RMI$_ENQNEWIN},
+    {4,   RMI$_ENQNEWOUT},
+    {4,   RMI$_ENQCVTLOC},
+    {4,   RMI$_ENQCVTIN},
+    {4,   RMI$_ENQCVTOUT},
+    {4,   RMI$_DEQLOC},
+    {4,   RMI$_DEQIN},
+    {4,   RMI$_DEQOUT},
+    {4,   RMI$_BLKLOC},
+    {4,   RMI$_BLKIN},
+    {4,   RMI$_BLKOUT},
+    {4,   RMI$_DIRIN},
+    {4,   RMI$_DIROUT},
+    /* We currently get a fault when trying these.  TODO: To be figured out. */
+#if 0
+    {140, RMI$_MSCP_EVERYTHING},   /* 35 32-bit words */
+    {152, RMI$_DDTM_ALL},          /* 38 32-bit words */
+    {80,  RMI$_TMSCP_EVERYTHING}   /* 20 32-bit words */
+#endif
+    {4,   RMI$_LPZ_PAGCNT},
+    {4,   RMI$_LPZ_HITS},
+    {4,   RMI$_LPZ_MISSES},
+    {4,   RMI$_LPZ_EXPCNT},
+    {4,   RMI$_LPZ_ALLOCF},
+    {4,   RMI$_LPZ_ALLOC2},
+    {4,   RMI$_ACCESS},
+    {4,   RMI$_ALLOC},
+    {4,   RMI$_FCPCREATE},
+    {4,   RMI$_VOLWAIT},
+    {4,   RMI$_FCPTURN},
+    {4,   RMI$_FCPERASE},
+    {4,   RMI$_OPENS},
+    {4,   RMI$_FIDHIT},
+    {4,   RMI$_FIDMISS},
+    {4,   RMI$_FILHDR_HIT},
+    {4,   RMI$_DIRFCB_HIT},
+    {4,   RMI$_DIRFCB_MISS},
+    {4,   RMI$_DIRDATA_HIT},
+    {4,   RMI$_EXTHIT},
+    {4,   RMI$_EXTMISS},
+    {4,   RMI$_QUOHIT},
+    {4,   RMI$_QUOMISS},
+    {4,   RMI$_STORAGMAP_HIT},
+    {4,   RMI$_VOLLCK},
+    {4,   RMI$_SYNCHLCK},
+    {4,   RMI$_SYNCHWAIT},
+    {4,   RMI$_ACCLCK},
+    {4,   RMI$_XQPCACHEWAIT},
+    {4,   RMI$_DIRDATA_MISS},
+    {4,   RMI$_FILHDR_MISS},
+    {4,   RMI$_STORAGMAP_MISS},
+    {4,   RMI$_PROCCNTMAX},
+    {4,   RMI$_PROCBATCNT},
+    {4,   RMI$_PROCINTCNT},
+    {4,   RMI$_PROCNETCNT},
+    {4,   RMI$_PROCSWITCHCNT},
+    {4,   RMI$_PROCBALSETCNT},
+    {4,   RMI$_PROCLOADCNT},
+    {4,   RMI$_BADFLTS},
+    {4,   RMI$_EXEFAULTS},
+    {4,   RMI$_HDRINSWAPS},
+    {4,   RMI$_HDROUTSWAPS},
+    {4,   RMI$_IOPAGCNT},
+    {4,   RMI$_ISWPCNTPG},
+    {4,   RMI$_OSWPCNT},
+    {4,   RMI$_OSWPCNTPG},
+    {4,   RMI$_RDFAULTS},
+    {4,   RMI$_TRANSFLTS},
+    {4,   RMI$_WRTFAULTS},
+#if defined __alpha
+    {4,   RMI$_USERPAGES},
+#endif
+    {4,   RMI$_VMSPAGES},
+    {4,   RMI$_TTWRITES},
+    {4,   RMI$_BUFOBJPAG},
+    {4,   RMI$_BUFOBJPAGPEAK},
+    {4,   RMI$_BUFOBJPAGS01},
+    {4,   RMI$_BUFOBJPAGS2},
+    {4,   RMI$_BUFOBJPAGMAXS01},
+    {4,   RMI$_BUFOBJPAGMAXS2},
+    {4,   RMI$_BUFOBJPAGPEAKS01},
+    {4,   RMI$_BUFOBJPAGPEAKS2},
+    {4,   RMI$_BUFOBJPGLTMAXS01},
+    {4,   RMI$_BUFOBJPGLTMAXS2},
+    {4,   RMI$_DLCK_INCMPLT},
+    {4,   RMI$_DLCKMSGS_IN},
+    {4,   RMI$_DLCKMSGS_OUT},
+    {4,   RMI$_MCHKERRS},
+    {4,   RMI$_MEMERRS},
+};
+
+static const struct item_st RMI_item_data_64bit[] = {
+#if defined __ia64
+    {8,   RMI$_FRLIST},
+    {8,   RMI$_MODLIST},
+#endif
+    {8,   RMI$_LCKMGR_REQCNT},
+    {8,   RMI$_LCKMGR_REQTIME},
+    {8,   RMI$_LCKMGR_SPINCNT},
+    {8,   RMI$_LCKMGR_SPINTIME},
+    {8,   RMI$_CPUINTSTK},
+    {8,   RMI$_CPUMPSYNCH},
+    {8,   RMI$_CPUKERNEL},
+    {8,   RMI$_CPUEXEC},
+    {8,   RMI$_CPUSUPER},
+    {8,   RMI$_CPUUSER},
+#if defined __ia64
+    {8,   RMI$_USERPAGES},
+#endif
+    {8,   RMI$_TQETOTAL},
+    {8,   RMI$_TQESYSUB},
+    {8,   RMI$_TQEUSRTIMR},
+    {8,   RMI$_TQEUSRWAKE},
+};
+
+static const struct item_st SYI_item_data[] = {
+    {4,   SYI$_PAGEFILE_FREE},
+};
+
+/*
+ * Input:
+ * items_data           - an array of lengths and codes
+ * items_data_num       - number of elements in that array
+ *
+ * Output:
+ * items                - pre-allocated ILE3 array to be filled.
+ *                        It's assumed to have items_data_num elements plus
+ *                        one extra for the terminating NULL element
+ * databuffer           - pre-allocated 32-bit word array.
+ *
+ * Returns the number of elements used in databuffer
+ */
+static size_t prepare_item_list(const struct item_st *items_input,
+                                size_t items_input_num,
+                                ILE3 *items,
+                                uint32_t__ptr32 databuffer)
 {
+    size_t data_sz = 0;
 
-    /* determine the number of items in the JPI array */
+    for (; items_input_num-- > 0; items_input++, items++) {
 
-    struct items_data_st item_entry;
-    int item_entry_count = sizeof(items_data)/sizeof(item_entry);
+        items->ile3$w_code = items_input->code;
+        /* Special treatment of JPI$_FINALEXC */
+        if (items->ile3$w_code == JPI$_FINALEXC)
+            items->ile3$w_length = 4;
+        else
+            items->ile3$w_length = items_input->length;
 
-    /* Create the JPI itemlist array to hold item_data content */
+        items->ile3$ps_bufaddr = databuffer;
+        items->ile3$ps_retlen_addr = 0;
 
-    struct {
-        short length, code;
-        int *buffer;
-        int *retlen;
-    } item[item_entry_count], *pitem; /* number of entries in items_data */
-
-    struct items_data_st *pitems_data;
-    int data_buffer[(item_entry_count*2)+4]; /* 8 bytes per entry max */
-    int iosb[2];
-    int sys_time[2];
-    int *ptr;
-    int i, j ;
-    int tmp_length   = 0;
-    int total_length = 0;
-
-    pitems_data = items_data;
-    pitem = item;
-
-
-    /* Setup itemlist for GETJPI */
-    while (pitems_data->length) {
-        pitem->length = pitems_data->length;
-        pitem->code   = pitems_data->code;
-        pitem->buffer = &data_buffer[total_length];
-        pitem->retlen = 0;
-        /* total_length is in longwords */
-        total_length += pitems_data->length/4;
-        pitems_data++;
-        pitem ++;
+        databuffer += items_input->length / sizeof(databuffer[0]);
+        data_sz += items_input->length;
     }
-    pitem->length = pitem->code = 0;
-
-    /* Fill data_buffer with various info bits from this process */
-    /* and twist that data to seed the SSL random number init    */
-
-    if (sys$getjpiw(EFN$C_ENF, NULL, NULL, item, &iosb, 0, 0) == SS$_NORMAL) {
-        for (i = 0; i < total_length; i++) {
-            sys$gettim((struct _generic_64 *)&sys_time[0]);
-            srand(sys_time[0] * data_buffer[0] * data_buffer[1] + i);
-
-            if (i == (total_length - 1)) { /* for JPI$_FINALEXC */
-                ptr = &data_buffer[i];
-                for (j = 0; j < 4; j++) {
-                    data_buffer[i + j] = ptr[j];
-                    /* OK to use rand() just to scramble the seed */
-                    data_buffer[i + j] ^= (sys_time[0] ^ rand());
-                    tmp_length++;
-                }
-            } else {
-                /* OK to use rand() just to scramble the seed */
-                data_buffer[i] ^= (sys_time[0] ^ rand());
-            }
+    /* Terminating NULL entry */
+    items->ile3$w_length = items->ile3$w_code = 0;
+    items->ile3$ps_bufaddr = items->ile3$ps_retlen_addr = NULL;
+
+    return data_sz / sizeof(databuffer[0]);
+}
+
+static void massage_JPI(ILE3 *items)
+{
+    /*
+     * Special treatment of JPI$_FINALEXC
+     * The result of that item's data buffer is a 32-bit address to a list of
+     * 4 32-bit words.
+     */
+    for (; items->ile3$w_length != 0; items++) {
+        if (items->ile3$w_code == JPI$_FINALEXC) {
+            uint32_t *data = items->ile3$ps_bufaddr;
+            uint32_t *ptr = (uint32_t *)*data;
+            size_t j;
+
+            /*
+             * We know we made space for 4 32-bit words, so we can do in-place
+             * replacement.
+             */
+            for (j = 0; j < 4; j++)
+                data[j] = ptr[j];
+
+            break;
+        }
+    }
+}
+
+/*
+ * This number expresses how many bits of data contain 1 bit of entropy.
+ *
+ * For the moment, we assume about 0.05 entropy bits per data bit, or 1
+ * bit of entropy per 20 data bits.
+ */
+#define ENTROPY_FACTOR  20
+
+size_t data_collect_method(RAND_POOL *pool)
+{
+    ILE3 JPI_items_64bit[OSSL_NELEM(JPI_item_data_64bit) + 1];
+    ILE3 RMI_items_64bit[OSSL_NELEM(RMI_item_data_64bit) + 1];
+    ILE3 DVI_items[OSSL_NELEM(DVI_item_data) + 1];
+    ILE3 JPI_items[OSSL_NELEM(JPI_item_data) + 1];
+    ILE3 RMI_items[OSSL_NELEM(RMI_item_data) + 1];
+    ILE3 SYI_items[OSSL_NELEM(SYI_item_data) + 1];
+    union {
+        /* This ensures buffer starts at 64 bit boundary */
+        uint64_t dummy;
+        uint32_t buffer[OSSL_NELEM(JPI_item_data_64bit) * 2
+                        + OSSL_NELEM(RMI_item_data_64bit) * 2
+                        + OSSL_NELEM(DVI_item_data)
+                        + OSSL_NELEM(JPI_item_data)
+                        + OSSL_NELEM(RMI_item_data)
+                        + OSSL_NELEM(SYI_item_data)
+                        + 4 /* For JPI$_FINALEXC */];
+    } data;
+    size_t total_elems = 0;
+    size_t total_length = 0;
+    size_t bytes_needed = rand_pool_bytes_needed(pool, ENTROPY_FACTOR);
+    size_t bytes_remaining = rand_pool_bytes_remaining(pool);
+
+    /* Take all the 64-bit items first, to ensure proper alignment of data */
+    total_elems +=
+        prepare_item_list(JPI_item_data_64bit, OSSL_NELEM(JPI_item_data_64bit),
+                          JPI_items_64bit, &data.buffer[total_elems]);
+    total_elems +=
+        prepare_item_list(RMI_item_data_64bit, OSSL_NELEM(RMI_item_data_64bit),
+                          RMI_items_64bit, &data.buffer[total_elems]);
+    /* Now the 32-bit items */
+    total_elems += prepare_item_list(DVI_item_data, OSSL_NELEM(DVI_item_data),
+                                     DVI_items, &data.buffer[total_elems]);
+    total_elems += prepare_item_list(JPI_item_data, OSSL_NELEM(JPI_item_data),
+                                     JPI_items, &data.buffer[total_elems]);
+    total_elems += prepare_item_list(RMI_item_data, OSSL_NELEM(RMI_item_data),
+                                     RMI_items, &data.buffer[total_elems]);
+    total_elems += prepare_item_list(SYI_item_data, OSSL_NELEM(SYI_item_data),
+                                     SYI_items, &data.buffer[total_elems]);
+    total_length = total_elems * sizeof(data.buffer[0]);
+
+    /* Fill data.buffer with various info bits from this process */
+    {
+        uint32_t status;
+        uint32_t efn;
+        IOSB iosb;
+        $DESCRIPTOR(SYSDEVICE,"SYS$SYSDEVICE:");
+
+        if ((status = sys$getdviw(EFN$C_ENF, 0, &SYSDEVICE, DVI_items,
+                                  0, 0, 0, 0, 0)) != SS$_NORMAL) {
+            lib$signal(status);
+            return 0;
+        }
+        if ((status = sys$getjpiw(EFN$C_ENF, 0, 0, JPI_items_64bit, 0, 0, 0))
+            != SS$_NORMAL) {
+            lib$signal(status);
+            return 0;
+        }
+        if ((status = sys$getjpiw(EFN$C_ENF, 0, 0, JPI_items, 0, 0, 0))
+            != SS$_NORMAL) {
+            lib$signal(status);
+            return 0;
+        }
+        if ((status = sys$getsyiw(EFN$C_ENF, 0, 0, SYI_items, 0, 0, 0))
+            != SS$_NORMAL) {
+            lib$signal(status);
+            return 0;
+        }
+        /*
+         * The RMI service is a bit special, as there is no synchronous
+         * variant, so we MUST create an event flag to synchronise on.
+         */
+        if ((status = lib$get_ef(&efn)) != SS$_NORMAL) {
+            lib$signal(status);
+            return 0;
+        }
+        if ((status = sys$getrmi(efn, 0, 0, RMI_items_64bit, &iosb, 0, 0))
+            != SS$_NORMAL) {
+            lib$signal(status);
+            return 0;
+        }
+        if ((status = sys$synch(efn, &iosb)) != SS$_NORMAL) {
+            lib$signal(status);
+            return 0;
+        }
+        if (iosb.iosb$l_getxxi_status != SS$_NORMAL) {
+            lib$signal(iosb.iosb$l_getxxi_status);
+            return 0;
         }
+        if ((status = sys$getrmi(efn, 0, 0, RMI_items, &iosb, 0, 0))
+            != SS$_NORMAL) {
+            lib$signal(status);
+            return 0;
+        }
+        if ((status = sys$synch(efn, &iosb)) != SS$_NORMAL) {
+            lib$signal(status);
+            return 0;
+        }
+        if (iosb.iosb$l_getxxi_status != SS$_NORMAL) {
+            lib$signal(iosb.iosb$l_getxxi_status);
+            return 0;
+        }
+        if ((status = lib$free_ef(&efn)) != SS$_NORMAL) {
+            lib$signal(status);
+            return 0;
+        }
+    }
 
-        total_length += (tmp_length - 1);
+    massage_JPI(JPI_items);
 
-        /* size of seed is total_length*4 bytes (64bytes) */
-        RAND_add((PTR_T) data_buffer, total_length*4, total_length * 2);
-    } else {
+    /*
+     * If we can't feed the requirements from the caller, we're in deep trouble.
+     */
+    if (!ossl_assert(total_length >= bytes_needed)) {
+        ERR_raise_data(ERR_LIB_RAND, RAND_R_RANDOM_POOL_UNDERFLOW,
+                       "Needed: %zu, Available: %zu",
+                       bytes_needed, total_length);
         return 0;
     }
 
-    return 1;
+    /*
+     * Try not to overfeed the pool
+     */
+    if (total_length > bytes_remaining)
+        total_length = bytes_remaining;
+
+    /* We give the pessimistic value for the amount of entropy */
+    rand_pool_add(pool, (unsigned char *)data.buffer, total_length,
+                  8 * total_length / ENTROPY_FACTOR);
+    return rand_pool_entropy_available(pool);
+}
+
+int rand_pool_add_nonce_data(RAND_POOL *pool)
+{
+    struct {
+        pid_t pid;
+        CRYPTO_THREAD_ID tid;
+        uint64_t time;
+    } data;
+
+    /* Erase the entire structure including any padding */
+    memset(&data, 0, sizeof(data));
+
+    /*
+     * Add process id, thread id, and a high resolution timestamp
+     * (where available, which is OpenVMS v8.4 and up) to ensure that
+     * the nonce is unique with high probability for different process
+     * instances.
+     */
+    data.pid = getpid();
+    data.tid = CRYPTO_THREAD_get_current_id();
+#if __CRTL_VER >= 80400000
+    sys$gettim_prec(&data.time);
+#else
+    sys$gettim((void*)&data.time);
+#endif
+
+    return rand_pool_add(pool, (unsigned char *)&data, sizeof(data), 0);
+}
+
+/*
+ * SYS$GET_ENTROPY METHOD
+ * ======================
+ *
+ * This is a high entropy method based on a new system service that is
+ * based on getentropy() from FreeBSD 12.  It's only used if available,
+ * and its availability is detected at run-time.
+ *
+ * We assume that this function provides full entropy random output.
+ */
+#define PUBLIC_VECTORS "SYS$LIBRARY:SYS$PUBLIC_VECTORS.EXE"
+#define GET_ENTROPY "SYS$GET_ENTROPY"
+
+static int get_entropy_address_flag = 0;
+static int (*get_entropy_address)(void *buffer, size_t buffer_size) = NULL;
+static int init_get_entropy_address(void)
+{
+    if (get_entropy_address_flag == 0)
+        get_entropy_address = dlsym(dlopen(PUBLIC_VECTORS, 0), GET_ENTROPY);
+    get_entropy_address_flag = 1;
+    return get_entropy_address != NULL;
+}
+
+size_t get_entropy_method(RAND_POOL *pool)
+{
+    /*
+     * The documentation says that SYS$GET_ENTROPY will give a maximum of
+     * 256 bytes of data.
+     */
+    unsigned char buffer[256];
+    size_t bytes_needed;
+    size_t bytes_to_get = 0;
+    uint32_t status;
+
+    for (bytes_needed = rand_pool_bytes_needed(pool, 1);
+         bytes_needed > 0;
+         bytes_needed -= bytes_to_get) {
+        bytes_to_get =
+            bytes_needed > sizeof(buffer) ? sizeof(buffer) : bytes_needed;
+
+        status = get_entropy_address(buffer, bytes_to_get);
+        if (status == SS$_RETRY) {
+            /* Set to zero so the loop doesn't diminish |bytes_needed| */
+            bytes_to_get = 0;
+            /* Should sleep some amount of time */
+            continue;
+        }
+
+        if (status != SS$_NORMAL) {
+            lib$signal(status);
+            return 0;
+        }
+
+        rand_pool_add(pool, buffer, bytes_to_get, 8 * bytes_to_get);
+    }
+
+    return rand_pool_entropy_available(pool);
 }
 
+/*
+ * MAIN ENTROPY ACQUISITION FUNCTIONS
+ * ==================================
+ *
+ * These functions are called by the RAND / DRBG functions
+ */
+
+size_t rand_pool_acquire_entropy(RAND_POOL *pool)
+{
+    if (init_get_entropy_address())
+        return get_entropy_method(pool);
+    return data_collect_method(pool);
+}
+
+
+int rand_pool_add_additional_data(RAND_POOL *pool)
+{
+    struct {
+        CRYPTO_THREAD_ID tid;
+        uint64_t time;
+    } data;
+
+    /* Erase the entire structure including any padding */
+    memset(&data, 0, sizeof(data));
+
+    /*
+     * Add some noise from the thread id and a high resolution timer.
+     * The thread id adds a little randomness if the drbg is accessed
+     * concurrently (which is the case for the <master> drbg).
+     */
+    data.tid = CRYPTO_THREAD_get_current_id();
+#if __CRTL_VER >= 80400000
+    sys$gettim_prec(&data.time);
+#else
+    sys$gettim((void*)&data.time);
 #endif
+
+    return rand_pool_add(pool, (unsigned char *)&data, sizeof(data), 0);
+}
+
+int rand_pool_init(void)
+{
+    return 1;
+}
+
+void rand_pool_cleanup(void)
+{
+}
+
+void rand_pool_keep_random_devices_open(int keep)
+{
+}