2 * Copyright 2001-2020 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 "rand_local.h"
29 #include <lib$routines.h>
31 # pragma message disable DOLLARID
34 #include <dlfcn.h> /* SYS$GET_ENTROPY presence */
36 #ifndef OPENSSL_RAND_SEED_OS
37 # error "Unsupported seeding method configured; must be os"
41 * DATA COLLECTION METHOD
42 * ======================
44 * This is a method to get low quality entropy.
45 * It works by collecting all kinds of statistical data that
46 * VMS offers and using them as random seed.
49 /* We need to make sure we have the right size pointer in some cases */
50 #if __INITIAL_POINTER_SIZE == 64
51 # pragma pointer_size save
52 # pragma pointer_size 32
54 typedef uint32_t *uint32_t__ptr32;
55 #if __INITIAL_POINTER_SIZE == 64
56 # pragma pointer_size restore
60 short length, code; /* length is number of bytes */
63 static const struct item_st DVI_item_data[] = {
68 static const struct item_st JPI_item_data[] = {
78 * Note: the direct result is just a 32-bit address. However, it points
79 * to a list of 4 32-bit words, so we make extra space for them so we can
80 * do in-place replacement of values
85 static const struct item_st JPI_item_data_64bit[] = {
86 {8, JPI$_LAST_LOGIN_I},
90 static const struct item_st RMI_item_data[] = {
162 /* We currently get a fault when trying these. TODO: To be figured out. */
164 {140, RMI$_MSCP_EVERYTHING}, /* 35 32-bit words */
165 {152, RMI$_DDTM_ALL}, /* 38 32-bit words */
166 {80, RMI$_TMSCP_EVERYTHING} /* 20 32-bit words */
168 {4, RMI$_LPZ_PAGCNT},
170 {4, RMI$_LPZ_MISSES},
171 {4, RMI$_LPZ_EXPCNT},
172 {4, RMI$_LPZ_ALLOCF},
173 {4, RMI$_LPZ_ALLOC2},
183 {4, RMI$_FILHDR_HIT},
184 {4, RMI$_DIRFCB_HIT},
185 {4, RMI$_DIRFCB_MISS},
186 {4, RMI$_DIRDATA_HIT},
191 {4, RMI$_STORAGMAP_HIT},
196 {4, RMI$_XQPCACHEWAIT},
197 {4, RMI$_DIRDATA_MISS},
198 {4, RMI$_FILHDR_MISS},
199 {4, RMI$_STORAGMAP_MISS},
200 {4, RMI$_PROCCNTMAX},
201 {4, RMI$_PROCBATCNT},
202 {4, RMI$_PROCINTCNT},
203 {4, RMI$_PROCNETCNT},
204 {4, RMI$_PROCSWITCHCNT},
205 {4, RMI$_PROCBALSETCNT},
206 {4, RMI$_PROCLOADCNT},
209 {4, RMI$_HDRINSWAPS},
210 {4, RMI$_HDROUTSWAPS},
224 {4, RMI$_BUFOBJPAGPEAK},
225 {4, RMI$_BUFOBJPAGS01},
226 {4, RMI$_BUFOBJPAGS2},
227 {4, RMI$_BUFOBJPAGMAXS01},
228 {4, RMI$_BUFOBJPAGMAXS2},
229 {4, RMI$_BUFOBJPAGPEAKS01},
230 {4, RMI$_BUFOBJPAGPEAKS2},
231 {4, RMI$_BUFOBJPGLTMAXS01},
232 {4, RMI$_BUFOBJPGLTMAXS2},
233 {4, RMI$_DLCK_INCMPLT},
234 {4, RMI$_DLCKMSGS_IN},
235 {4, RMI$_DLCKMSGS_OUT},
240 static const struct item_st RMI_item_data_64bit[] = {
245 {8, RMI$_LCKMGR_REQCNT},
246 {8, RMI$_LCKMGR_REQTIME},
247 {8, RMI$_LCKMGR_SPINCNT},
248 {8, RMI$_LCKMGR_SPINTIME},
250 {8, RMI$_CPUMPSYNCH},
260 {8, RMI$_TQEUSRTIMR},
261 {8, RMI$_TQEUSRWAKE},
264 static const struct item_st SYI_item_data[] = {
265 {4, SYI$_PAGEFILE_FREE},
270 * items_data - an array of lengths and codes
271 * items_data_num - number of elements in that array
274 * items - pre-allocated ILE3 array to be filled.
275 * It's assumed to have items_data_num elements plus
276 * one extra for the terminating NULL element
277 * databuffer - pre-allocated 32-bit word array.
279 * Returns the number of elements used in databuffer
281 static size_t prepare_item_list(const struct item_st *items_input,
282 size_t items_input_num,
284 uint32_t__ptr32 databuffer)
288 for (; items_input_num-- > 0; items_input++, items++) {
290 items->ile3$w_code = items_input->code;
291 /* Special treatment of JPI$_FINALEXC */
292 if (items->ile3$w_code == JPI$_FINALEXC)
293 items->ile3$w_length = 4;
295 items->ile3$w_length = items_input->length;
297 items->ile3$ps_bufaddr = databuffer;
298 items->ile3$ps_retlen_addr = 0;
300 databuffer += items_input->length / sizeof(databuffer[0]);
301 data_sz += items_input->length;
303 /* Terminating NULL entry */
304 items->ile3$w_length = items->ile3$w_code = 0;
305 items->ile3$ps_bufaddr = items->ile3$ps_retlen_addr = NULL;
307 return data_sz / sizeof(databuffer[0]);
310 static void massage_JPI(ILE3 *items)
313 * Special treatment of JPI$_FINALEXC
314 * The result of that item's data buffer is a 32-bit address to a list of
317 for (; items->ile3$w_length != 0; items++) {
318 if (items->ile3$w_code == JPI$_FINALEXC) {
319 uint32_t *data = items->ile3$ps_bufaddr;
320 uint32_t *ptr = (uint32_t *)*data;
324 * We know we made space for 4 32-bit words, so we can do in-place
327 for (j = 0; j < 4; j++)
336 * This number expresses how many bits of data contain 1 bit of entropy.
338 * For the moment, we assume about 0.05 entropy bits per data bit, or 1
339 * bit of entropy per 20 data bits.
341 #define ENTROPY_FACTOR 20
343 size_t data_collect_method(RAND_POOL *pool)
345 ILE3 JPI_items_64bit[OSSL_NELEM(JPI_item_data_64bit) + 1];
346 ILE3 RMI_items_64bit[OSSL_NELEM(RMI_item_data_64bit) + 1];
347 ILE3 DVI_items[OSSL_NELEM(DVI_item_data) + 1];
348 ILE3 JPI_items[OSSL_NELEM(JPI_item_data) + 1];
349 ILE3 RMI_items[OSSL_NELEM(RMI_item_data) + 1];
350 ILE3 SYI_items[OSSL_NELEM(SYI_item_data) + 1];
352 /* This ensures buffer starts at 64 bit boundary */
354 uint32_t buffer[OSSL_NELEM(JPI_item_data_64bit) * 2
355 + OSSL_NELEM(RMI_item_data_64bit) * 2
356 + OSSL_NELEM(DVI_item_data)
357 + OSSL_NELEM(JPI_item_data)
358 + OSSL_NELEM(RMI_item_data)
359 + OSSL_NELEM(SYI_item_data)
360 + 4 /* For JPI$_FINALEXC */];
362 size_t total_elems = 0;
363 size_t total_length = 0;
364 size_t bytes_needed = rand_pool_bytes_needed(pool, ENTROPY_FACTOR);
365 size_t bytes_remaining = rand_pool_bytes_remaining(pool);
367 /* Take all the 64-bit items first, to ensure proper alignment of data */
369 prepare_item_list(JPI_item_data_64bit, OSSL_NELEM(JPI_item_data_64bit),
370 JPI_items_64bit, &data.buffer[total_elems]);
372 prepare_item_list(RMI_item_data_64bit, OSSL_NELEM(RMI_item_data_64bit),
373 RMI_items_64bit, &data.buffer[total_elems]);
374 /* Now the 32-bit items */
375 total_elems += prepare_item_list(DVI_item_data, OSSL_NELEM(DVI_item_data),
376 DVI_items, &data.buffer[total_elems]);
377 total_elems += prepare_item_list(JPI_item_data, OSSL_NELEM(JPI_item_data),
378 JPI_items, &data.buffer[total_elems]);
379 total_elems += prepare_item_list(RMI_item_data, OSSL_NELEM(RMI_item_data),
380 RMI_items, &data.buffer[total_elems]);
381 total_elems += prepare_item_list(SYI_item_data, OSSL_NELEM(SYI_item_data),
382 SYI_items, &data.buffer[total_elems]);
383 total_length = total_elems * sizeof(data.buffer[0]);
385 /* Fill data.buffer with various info bits from this process */
390 $DESCRIPTOR(SYSDEVICE,"SYS$SYSDEVICE:");
392 if ((status = sys$getdviw(EFN$C_ENF, 0, &SYSDEVICE, DVI_items,
393 0, 0, 0, 0, 0)) != SS$_NORMAL) {
397 if ((status = sys$getjpiw(EFN$C_ENF, 0, 0, JPI_items_64bit, 0, 0, 0))
402 if ((status = sys$getjpiw(EFN$C_ENF, 0, 0, JPI_items, 0, 0, 0))
407 if ((status = sys$getsyiw(EFN$C_ENF, 0, 0, SYI_items, 0, 0, 0))
413 * The RMI service is a bit special, as there is no synchronous
414 * variant, so we MUST create an event flag to synchronise on.
416 if ((status = lib$get_ef(&efn)) != SS$_NORMAL) {
420 if ((status = sys$getrmi(efn, 0, 0, RMI_items_64bit, &iosb, 0, 0))
425 if ((status = sys$synch(efn, &iosb)) != SS$_NORMAL) {
429 if (iosb.iosb$l_getxxi_status != SS$_NORMAL) {
430 lib$signal(iosb.iosb$l_getxxi_status);
433 if ((status = sys$getrmi(efn, 0, 0, RMI_items, &iosb, 0, 0))
438 if ((status = sys$synch(efn, &iosb)) != SS$_NORMAL) {
442 if (iosb.iosb$l_getxxi_status != SS$_NORMAL) {
443 lib$signal(iosb.iosb$l_getxxi_status);
446 if ((status = lib$free_ef(&efn)) != SS$_NORMAL) {
452 massage_JPI(JPI_items);
455 * If we can't feed the requirements from the caller, we're in deep trouble.
457 if (!ossl_assert(total_length >= bytes_needed)) {
458 ERR_raise_data(ERR_LIB_RAND, RAND_R_RANDOM_POOL_UNDERFLOW,
459 "Needed: %zu, Available: %zu",
460 bytes_needed, total_length);
465 * Try not to overfeed the pool
467 if (total_length > bytes_remaining)
468 total_length = bytes_remaining;
470 /* We give the pessimistic value for the amount of entropy */
471 rand_pool_add(pool, (unsigned char *)data.buffer, total_length,
472 8 * total_length / ENTROPY_FACTOR);
473 return rand_pool_entropy_available(pool);
476 int rand_pool_add_nonce_data(RAND_POOL *pool)
480 CRYPTO_THREAD_ID tid;
484 /* Erase the entire structure including any padding */
485 memset(&data, 0, sizeof(data));
488 * Add process id, thread id, and a high resolution timestamp
489 * (where available, which is OpenVMS v8.4 and up) to ensure that
490 * the nonce is unique with high probability for different process
494 data.tid = CRYPTO_THREAD_get_current_id();
495 #if __CRTL_VER >= 80400000
496 sys$gettim_prec(&data.time);
498 sys$gettim((void*)&data.time);
501 return rand_pool_add(pool, (unsigned char *)&data, sizeof(data), 0);
505 * SYS$GET_ENTROPY METHOD
506 * ======================
508 * This is a high entropy method based on a new system service that is
509 * based on getentropy() from FreeBSD 12. It's only used if available,
510 * and its availability is detected at run-time.
512 * We assume that this function provides full entropy random output.
514 #define PUBLIC_VECTORS "SYS$LIBRARY:SYS$PUBLIC_VECTORS.EXE"
515 #define GET_ENTROPY "SYS$GET_ENTROPY"
517 static int get_entropy_address_flag = 0;
518 static int (*get_entropy_address)(void *buffer, size_t buffer_size) = NULL;
519 static int init_get_entropy_address(void)
521 if (get_entropy_address_flag == 0)
522 get_entropy_address = dlsym(dlopen(PUBLIC_VECTORS, 0), GET_ENTROPY);
523 get_entropy_address_flag = 1;
524 return get_entropy_address != NULL;
527 size_t get_entropy_method(RAND_POOL *pool)
530 * The documentation says that SYS$GET_ENTROPY will give a maximum of
533 unsigned char buffer[256];
535 size_t bytes_to_get = 0;
538 for (bytes_needed = rand_pool_bytes_needed(pool, 1);
540 bytes_needed -= bytes_to_get) {
542 bytes_needed > sizeof(buffer) ? sizeof(buffer) : bytes_needed;
544 status = get_entropy_address(buffer, bytes_to_get);
545 if (status == SS$_RETRY) {
546 /* Set to zero so the loop doesn't diminish |bytes_needed| */
548 /* Should sleep some amount of time */
552 if (status != SS$_NORMAL) {
557 rand_pool_add(pool, buffer, bytes_to_get, 8 * bytes_to_get);
560 return rand_pool_entropy_available(pool);
564 * MAIN ENTROPY ACQUISITION FUNCTIONS
565 * ==================================
567 * These functions are called by the RAND / DRBG functions
570 size_t rand_pool_acquire_entropy(RAND_POOL *pool)
572 if (init_get_entropy_address())
573 return get_entropy_method(pool);
574 return data_collect_method(pool);
578 int rand_pool_add_additional_data(RAND_POOL *pool)
581 CRYPTO_THREAD_ID tid;
585 /* Erase the entire structure including any padding */
586 memset(&data, 0, sizeof(data));
589 * Add some noise from the thread id and a high resolution timer.
590 * The thread id adds a little randomness if the drbg is accessed
591 * concurrently (which is the case for the <master> drbg).
593 data.tid = CRYPTO_THREAD_get_current_id();
594 #if __CRTL_VER >= 80400000
595 sys$gettim_prec(&data.time);
597 sys$gettim((void*)&data.time);
600 return rand_pool_add(pool, (unsigned char *)&data, sizeof(data), 0);
603 int rand_pool_init(void)
608 void rand_pool_cleanup(void)
612 void rand_pool_keep_random_devices_open(int keep)