2 * Copyright 2001-2021 The OpenSSL Project Authors. All Rights Reserved.
4 * Licensed under the Apache License 2.0 (the "License"). You may not use
5 * this file except in compliance with the License. You can obtain a copy
6 * in the file LICENSE in the source distribution or at
7 * https://www.openssl.org/source/license.html
12 #define __NEW_STARLET 1 /* New starlet definitions since VMS 7.0 */
14 #include "internal/cryptlib.h"
15 #include <openssl/rand.h>
16 #include "crypto/rand.h"
17 #include "crypto/rand_pool.h"
18 #include "prov/seeding.h"
30 #include <lib$routines.h>
32 # pragma message disable DOLLARID
35 #include <dlfcn.h> /* SYS$GET_ENTROPY presence */
37 #ifndef OPENSSL_RAND_SEED_OS
38 # error "Unsupported seeding method configured; must be os"
42 * DATA COLLECTION METHOD
43 * ======================
45 * This is a method to get low quality entropy.
46 * It works by collecting all kinds of statistical data that
47 * VMS offers and using them as random seed.
50 /* We need to make sure we have the right size pointer in some cases */
51 #if __INITIAL_POINTER_SIZE == 64
52 # pragma pointer_size save
53 # pragma pointer_size 32
55 typedef uint32_t *uint32_t__ptr32
;
56 #if __INITIAL_POINTER_SIZE == 64
57 # pragma pointer_size restore
61 short length
, code
; /* length is number of bytes */
64 static const struct item_st DVI_item_data
[] = {
69 static const struct item_st JPI_item_data
[] = {
79 * Note: the direct result is just a 32-bit address. However, it points
80 * to a list of 4 32-bit words, so we make extra space for them so we can
81 * do in-place replacement of values
86 static const struct item_st JPI_item_data_64bit
[] = {
87 {8, JPI$_LAST_LOGIN_I
},
91 static const struct item_st RMI_item_data
[] = {
163 /* We currently get a fault when trying these. TODO: To be figured out. */
165 {140, RMI$_MSCP_EVERYTHING
}, /* 35 32-bit words */
166 {152, RMI$_DDTM_ALL
}, /* 38 32-bit words */
167 {80, RMI$_TMSCP_EVERYTHING
} /* 20 32-bit words */
169 {4, RMI$_LPZ_PAGCNT
},
171 {4, RMI$_LPZ_MISSES
},
172 {4, RMI$_LPZ_EXPCNT
},
173 {4, RMI$_LPZ_ALLOCF
},
174 {4, RMI$_LPZ_ALLOC2
},
184 {4, RMI$_FILHDR_HIT
},
185 {4, RMI$_DIRFCB_HIT
},
186 {4, RMI$_DIRFCB_MISS
},
187 {4, RMI$_DIRDATA_HIT
},
192 {4, RMI$_STORAGMAP_HIT
},
197 {4, RMI$_XQPCACHEWAIT
},
198 {4, RMI$_DIRDATA_MISS
},
199 {4, RMI$_FILHDR_MISS
},
200 {4, RMI$_STORAGMAP_MISS
},
201 {4, RMI$_PROCCNTMAX
},
202 {4, RMI$_PROCBATCNT
},
203 {4, RMI$_PROCINTCNT
},
204 {4, RMI$_PROCNETCNT
},
205 {4, RMI$_PROCSWITCHCNT
},
206 {4, RMI$_PROCBALSETCNT
},
207 {4, RMI$_PROCLOADCNT
},
210 {4, RMI$_HDRINSWAPS
},
211 {4, RMI$_HDROUTSWAPS
},
225 {4, RMI$_BUFOBJPAGPEAK
},
226 {4, RMI$_BUFOBJPAGS01
},
227 {4, RMI$_BUFOBJPAGS2
},
228 {4, RMI$_BUFOBJPAGMAXS01
},
229 {4, RMI$_BUFOBJPAGMAXS2
},
230 {4, RMI$_BUFOBJPAGPEAKS01
},
231 {4, RMI$_BUFOBJPAGPEAKS2
},
232 {4, RMI$_BUFOBJPGLTMAXS01
},
233 {4, RMI$_BUFOBJPGLTMAXS2
},
234 {4, RMI$_DLCK_INCMPLT
},
235 {4, RMI$_DLCKMSGS_IN
},
236 {4, RMI$_DLCKMSGS_OUT
},
241 static const struct item_st RMI_item_data_64bit
[] = {
246 {8, RMI$_LCKMGR_REQCNT
},
247 {8, RMI$_LCKMGR_REQTIME
},
248 {8, RMI$_LCKMGR_SPINCNT
},
249 {8, RMI$_LCKMGR_SPINTIME
},
251 {8, RMI$_CPUMPSYNCH
},
261 {8, RMI$_TQEUSRTIMR
},
262 {8, RMI$_TQEUSRWAKE
},
265 static const struct item_st SYI_item_data
[] = {
266 {4, SYI$_PAGEFILE_FREE
},
271 * items_data - an array of lengths and codes
272 * items_data_num - number of elements in that array
275 * items - pre-allocated ILE3 array to be filled.
276 * It's assumed to have items_data_num elements plus
277 * one extra for the terminating NULL element
278 * databuffer - pre-allocated 32-bit word array.
280 * Returns the number of elements used in databuffer
282 static size_t prepare_item_list(const struct item_st
*items_input
,
283 size_t items_input_num
,
285 uint32_t__ptr32 databuffer
)
289 for (; items_input_num
-- > 0; items_input
++, items
++) {
291 items
->ile3$w_code
= items_input
->code
;
292 /* Special treatment of JPI$_FINALEXC */
293 if (items
->ile3$w_code
== JPI$_FINALEXC
)
294 items
->ile3$w_length
= 4;
296 items
->ile3$w_length
= items_input
->length
;
298 items
->ile3$ps_bufaddr
= databuffer
;
299 items
->ile3$ps_retlen_addr
= 0;
301 databuffer
+= items_input
->length
/ sizeof(databuffer
[0]);
302 data_sz
+= items_input
->length
;
304 /* Terminating NULL entry */
305 items
->ile3$w_length
= items
->ile3$w_code
= 0;
306 items
->ile3$ps_bufaddr
= items
->ile3$ps_retlen_addr
= NULL
;
308 return data_sz
/ sizeof(databuffer
[0]);
311 static void massage_JPI(ILE3
*items
)
314 * Special treatment of JPI$_FINALEXC
315 * The result of that item's data buffer is a 32-bit address to a list of
318 for (; items
->ile3$w_length
!= 0; items
++) {
319 if (items
->ile3$w_code
== JPI$_FINALEXC
) {
320 uint32_t *data
= items
->ile3$ps_bufaddr
;
321 uint32_t *ptr
= (uint32_t *)*data
;
325 * We know we made space for 4 32-bit words, so we can do in-place
328 for (j
= 0; j
< 4; j
++)
337 * This number expresses how many bits of data contain 1 bit of entropy.
339 * For the moment, we assume about 0.05 entropy bits per data bit, or 1
340 * bit of entropy per 20 data bits.
342 #define ENTROPY_FACTOR 20
344 size_t data_collect_method(RAND_POOL
*pool
)
346 ILE3 JPI_items_64bit
[OSSL_NELEM(JPI_item_data_64bit
) + 1];
347 ILE3 RMI_items_64bit
[OSSL_NELEM(RMI_item_data_64bit
) + 1];
348 ILE3 DVI_items
[OSSL_NELEM(DVI_item_data
) + 1];
349 ILE3 JPI_items
[OSSL_NELEM(JPI_item_data
) + 1];
350 ILE3 RMI_items
[OSSL_NELEM(RMI_item_data
) + 1];
351 ILE3 SYI_items
[OSSL_NELEM(SYI_item_data
) + 1];
353 /* This ensures buffer starts at 64 bit boundary */
355 uint32_t buffer
[OSSL_NELEM(JPI_item_data_64bit
) * 2
356 + OSSL_NELEM(RMI_item_data_64bit
) * 2
357 + OSSL_NELEM(DVI_item_data
)
358 + OSSL_NELEM(JPI_item_data
)
359 + OSSL_NELEM(RMI_item_data
)
360 + OSSL_NELEM(SYI_item_data
)
361 + 4 /* For JPI$_FINALEXC */];
363 size_t total_elems
= 0;
364 size_t total_length
= 0;
365 size_t bytes_needed
= ossl_rand_pool_bytes_needed(pool
, ENTROPY_FACTOR
);
366 size_t bytes_remaining
= ossl_rand_pool_bytes_remaining(pool
);
368 /* Take all the 64-bit items first, to ensure proper alignment of data */
370 prepare_item_list(JPI_item_data_64bit
, OSSL_NELEM(JPI_item_data_64bit
),
371 JPI_items_64bit
, &data
.buffer
[total_elems
]);
373 prepare_item_list(RMI_item_data_64bit
, OSSL_NELEM(RMI_item_data_64bit
),
374 RMI_items_64bit
, &data
.buffer
[total_elems
]);
375 /* Now the 32-bit items */
376 total_elems
+= prepare_item_list(DVI_item_data
, OSSL_NELEM(DVI_item_data
),
377 DVI_items
, &data
.buffer
[total_elems
]);
378 total_elems
+= prepare_item_list(JPI_item_data
, OSSL_NELEM(JPI_item_data
),
379 JPI_items
, &data
.buffer
[total_elems
]);
380 total_elems
+= prepare_item_list(RMI_item_data
, OSSL_NELEM(RMI_item_data
),
381 RMI_items
, &data
.buffer
[total_elems
]);
382 total_elems
+= prepare_item_list(SYI_item_data
, OSSL_NELEM(SYI_item_data
),
383 SYI_items
, &data
.buffer
[total_elems
]);
384 total_length
= total_elems
* sizeof(data
.buffer
[0]);
386 /* Fill data.buffer with various info bits from this process */
391 $
DESCRIPTOR(SYSDEVICE
,"SYS$SYSDEVICE:");
393 if ((status
= sys$
getdviw(EFN$C_ENF
, 0, &SYSDEVICE
, DVI_items
,
394 0, 0, 0, 0, 0)) != SS$_NORMAL
) {
398 if ((status
= sys$
getjpiw(EFN$C_ENF
, 0, 0, JPI_items_64bit
, 0, 0, 0))
403 if ((status
= sys$
getjpiw(EFN$C_ENF
, 0, 0, JPI_items
, 0, 0, 0))
408 if ((status
= sys$
getsyiw(EFN$C_ENF
, 0, 0, SYI_items
, 0, 0, 0))
414 * The RMI service is a bit special, as there is no synchronous
415 * variant, so we MUST create an event flag to synchronise on.
417 if ((status
= lib$
get_ef(&efn
)) != SS$_NORMAL
) {
421 if ((status
= sys$
getrmi(efn
, 0, 0, RMI_items_64bit
, &iosb
, 0, 0))
426 if ((status
= sys$
synch(efn
, &iosb
)) != SS$_NORMAL
) {
430 if (iosb
.iosb$l_getxxi_status
!= SS$_NORMAL
) {
431 lib$
signal(iosb
.iosb$l_getxxi_status
);
434 if ((status
= sys$
getrmi(efn
, 0, 0, RMI_items
, &iosb
, 0, 0))
439 if ((status
= sys$
synch(efn
, &iosb
)) != SS$_NORMAL
) {
443 if (iosb
.iosb$l_getxxi_status
!= SS$_NORMAL
) {
444 lib$
signal(iosb
.iosb$l_getxxi_status
);
447 if ((status
= lib$
free_ef(&efn
)) != SS$_NORMAL
) {
453 massage_JPI(JPI_items
);
456 * If we can't feed the requirements from the caller, we're in deep trouble.
458 if (!ossl_assert(total_length
>= bytes_needed
)) {
459 ERR_raise_data(ERR_LIB_RAND
, RAND_R_RANDOM_POOL_UNDERFLOW
,
460 "Needed: %zu, Available: %zu",
461 bytes_needed
, total_length
);
466 * Try not to overfeed the pool
468 if (total_length
> bytes_remaining
)
469 total_length
= bytes_remaining
;
471 /* We give the pessimistic value for the amount of entropy */
472 ossl_rand_pool_add(pool
, (unsigned char *)data
.buffer
, total_length
,
473 8 * total_length
/ ENTROPY_FACTOR
);
474 return ossl_rand_pool_entropy_available(pool
);
477 int ossl_pool_add_nonce_data(RAND_POOL
*pool
)
481 CRYPTO_THREAD_ID tid
;
485 /* Erase the entire structure including any padding */
486 memset(&data
, 0, sizeof(data
));
489 * Add process id, thread id, and a high resolution timestamp
490 * (where available, which is OpenVMS v8.4 and up) to ensure that
491 * the nonce is unique with high probability for different process
495 data
.tid
= CRYPTO_THREAD_get_current_id();
496 #if __CRTL_VER >= 80400000
497 sys$
gettim_prec(&data
.time
);
499 sys$
gettim((void*)&data
.time
);
502 return ossl_rand_pool_add(pool
, (unsigned char *)&data
, sizeof(data
), 0);
506 * SYS$GET_ENTROPY METHOD
507 * ======================
509 * This is a high entropy method based on a new system service that is
510 * based on getentropy() from FreeBSD 12. It's only used if available,
511 * and its availability is detected at run-time.
513 * We assume that this function provides full entropy random output.
515 #define PUBLIC_VECTORS "SYS$LIBRARY:SYS$PUBLIC_VECTORS.EXE"
516 #define GET_ENTROPY "SYS$GET_ENTROPY"
518 static int get_entropy_address_flag
= 0;
519 static int (*get_entropy_address
)(void *buffer
, size_t buffer_size
) = NULL
;
520 static int init_get_entropy_address(void)
522 if (get_entropy_address_flag
== 0)
523 get_entropy_address
= dlsym(dlopen(PUBLIC_VECTORS
, 0), GET_ENTROPY
);
524 get_entropy_address_flag
= 1;
525 return get_entropy_address
!= NULL
;
528 size_t get_entropy_method(RAND_POOL
*pool
)
531 * The documentation says that SYS$GET_ENTROPY will give a maximum of
534 unsigned char buffer
[256];
536 size_t bytes_to_get
= 0;
539 for (bytes_needed
= ossl_rand_pool_bytes_needed(pool
, 1);
541 bytes_needed
-= bytes_to_get
) {
543 bytes_needed
> sizeof(buffer
) ? sizeof(buffer
) : bytes_needed
;
545 status
= get_entropy_address(buffer
, bytes_to_get
);
546 if (status
== SS$_RETRY
) {
547 /* Set to zero so the loop doesn't diminish |bytes_needed| */
549 /* Should sleep some amount of time */
553 if (status
!= SS$_NORMAL
) {
558 ossl_rand_pool_add(pool
, buffer
, bytes_to_get
, 8 * bytes_to_get
);
561 return ossl_rand_pool_entropy_available(pool
);
565 * MAIN ENTROPY ACQUISITION FUNCTIONS
566 * ==================================
568 * These functions are called by the RAND / DRBG functions
571 size_t ossl_pool_acquire_entropy(RAND_POOL
*pool
)
573 if (init_get_entropy_address())
574 return get_entropy_method(pool
);
575 return data_collect_method(pool
);
579 int ossl_rand_pool_add_additional_data(RAND_POOL
*pool
)
582 CRYPTO_THREAD_ID tid
;
586 /* Erase the entire structure including any padding */
587 memset(&data
, 0, sizeof(data
));
590 * Add some noise from the thread id and a high resolution timer.
591 * The thread id adds a little randomness if the drbg is accessed
592 * concurrently (which is the case for the <master> drbg).
594 data
.tid
= CRYPTO_THREAD_get_current_id();
595 #if __CRTL_VER >= 80400000
596 sys$
gettim_prec(&data
.time
);
598 sys$
gettim((void*)&data
.time
);
601 return ossl_rand_pool_add(pool
, (unsigned char *)&data
, sizeof(data
), 0);
604 int ossl_rand_pool_init(void)
609 void ossl_rand_pool_cleanup(void)
613 void ossl_rand_pool_keep_random_devices_open(int keep
)