Random and ‘Almost Random’ Numbers on z/OS
Random numbers can come in handy. They can be used to create a unique filename, generate a random new password, or randomly select a record. The good news is that we can obtain random numbers on z/OS. Well, at least some of them are random.
COBOL and RANDOM
A quick look at the IBM Enterprise COBOL manuals will show that there is a RANDOM function: sounds perfect. So, let’s run some code to use this function:
Compute WS-RNDNUM-L1 = Function RANDOM(1) Display 'Number 1: ' WS-RNDNUM-L1 Compute WS-RNDNUM-L1 = Function RANDOM(1) Display 'Number 2: ' WS-RNDNUM-L1 Compute WS-RNDNUM-L1 = Function RANDOM(1) Display 'Number 3: ' WS-RNDNUM-L1
Number 1: .88541431021197434E 00 Number 2: .88541431021197434E 00 Number 3: .88541431021197434E 00
Because we specified a seed for every RANDOM call, COBOL restarted the sequence each time. To get a sequence with different numbers, we only specify the seed for the first call:
Compute WS-RNDNUM-L1 = Function RANDOM(1) Display 'Number 1: ' WS-RNDNUM-L1 Compute WS-RNDNUM-L1 = Function RANDOM Display 'Number 2: ' WS-RNDNUM-L1 Compute WS-RNDNUM-L1 = Function RANDOM Display 'Number 3: ' WS-RNDNUM-L1
Number 1: .88541431021197434E 00 Number 2: .12016591714702822E 00 Number 3: .60956745949088011E 00
Number 1: .88541431021197434E 00 Number 2: .12016591714702822E 00 Number 3: .60956745949088011E 00
01 WS-RNDNUM Pic 9(2). Compute WS-RNDNUM-L1 = Function RANDOM(1) Compute WS-RNDNUM = WS-RNDNUM-L1*100
Other Languages With RANDOM Functions
COBOL isn’t the only programming language with a RANDOM function or statement. In fact, most languages have something. For example:
- PL/I: RANDOM
- C/C++: random(), rand(), rand_r(), drand48(), erand48(), lrand48(), mrand48(), nrand48(), jrand48()
- SAS: rand
- REXX: RANDOM
And the options don’t stop there. Db2 has a RANDOM function, and z/OS Language Environment provides the CEERAN0 program. Let’s look at some COBOL code to call CEERAN0:
* Get random number between 0 and 1. Call 'CEERAN0' Using WS-SEED, WS-RNDNUM-L1, WS-FC * Handle any errors calling our function On Exception Display 'CBLRAND: Error calling CEERAN0' Move 12 To WS-FC-SEVERITY End-Call If WS-FC-SEVERITY = 0 Then * Call successful Display 'LE Number 1: ' WS-RNDNUM-L1 Else * Error attempting to call Display 'CBLRAND: Call to CEERAN0 failed, msg ' WS-FC-MSG-NO End-If
Like the COBOL RANDOM function, we pass CEERAN0 a seed. Suppose we set this seed to 1 (the same as our COBOL RANDOM function), and run it on the same z/OS system. We get:
LE Number 1: .44270715510598717E 00
This number is different to the COBOL RANDOM function output, even though we use the same seed. Different programming languages and other functions often use different methods to create a pseudo random number sequence. The Enterprise COBOL RANDOM function uses something called the “rectangular distribution,” while CEERAN0 uses the “multiplicative congruential method.” We won’t go into the mathematical details, but you can find information about them on the web.
Either way, CEERAN0 and other language-based random functions all use some kind of mathematical process to get the pseudo random sequence. So, the chances are that if you run the same code, with the same seed, on different z/OS systems or CECs, you’ll get the same sequence of numbers.
A Simple Random Number
This is great if you want to create a sequence of random numbers. However, what if we just want a single random number?
To do this, we need a relatively random seed. Yes, we need a random number to get a random number. But that random seed doesn’t need to be all that random: “kind of” random works. For example, we could ask the user to specify a seed in a parameter file. Or we could generate a seed from a number that regularly changes. One idea is to pass a time, or datetime value as the seed.
In our COBOL code, we could do this as follows:
* Get current time Move Function Current-Date(9:8) to WS-HHMMSSTT * Get random number between 0 and 1, using time as seed Compute WS-RNDNUM-L1 = Function RANDOM(WS-HHMMSSTT) Running this code twice in the same COBOL program produces something like: Number 1: .70938615347695823E 00 Number 2: .15209330858294540E 00
The same tactic can be used for any function that outputs a pseudo-random sequence.
Anyone used to working with UNIX will now be jumping up and down, and screaming “/dev/random!” And they have a point. The device /dev/random or /dev/urandom have been used for a long time by UNIX applications to return random numbers. As z/OS is also a POSIX certified OS, you’d expect it to have something similar, and you won’t be disappointed.
/dev/random and /dev/urandom are interesting. Traditionally, they calculated random data from environmental noise from device drivers. /dev/random may make a program wait if there is not enough “noise” collected; /dev/urandom will do the best it can. Today, OSes can choose their own method.
/dev/random and friends produce random data, not random numbers. The following command uses the z/OS UNIX shell command od to show 8 bytes from /dev/urandom as hexadecimal numbers:
DZS:/: >od -An -tx -N8 /dev/urandom 4FBB64B4 6422427F DZS:/: >
So, we’ll need to do some work in our program if we want to convert this to a number.
The good news is that /dev/random and friends are not pseudo-random number generators: they produce ‘true’ random data. Every time we issue this od command, we’ll get different output.
ICSF Features
Before the z14 mainframe, z/OS used ICSF for /dev/random. ICSF is the z/OS component dealing with encryption and decryption, and it provides its own APIs to return a true random number. Here’s an example of a COBOL program doing exactly that:
01 WS-RC Pic 9(8) Comp. 01 WS-REAS Pic 9(8) Comp. 01 WS-EXITL Pic 9(8) Comp Value 0. 01 WS-EXIT Pic X(4). 01 WS-RANDNUM Pic X(8). 01 WS-FORM Pic X(8) Value 'RANDOM '. Procedure Division. Call 'CSNBRNG' Using WS-RC, WS-REAS, WS-EXITL, WS-EXIT, WS-FORM, WS-RANDNUM Display 'Number: ' WS-RANDNUM
This pretty much does what /dev/random did: return 8 bytes of true random data. The CSNERNG program is similar, but allows the caller to determine the length of data to return. According to the z/OS manuals, ICSF uses a “time variant input with a very low probability of recycling” as the basis for calculating this random data.
These ICSF programs (and /dev/random when it relies on ICSF) have a couple of issues. Firstly, the CSF started task must be up and running to use these any other ICSF functions. Secondly, the program will need RACF authority to call the CSNBRNG/CSNERNG programs. Finally, ICSF needs certain cryptographic features for the IBM Z mainframe enabled.
Advantages of the z14
In 2017, IBM announced the new z14 mainframe. Together with z/OS 2.3, this heralded the beginning of pervasive encryption: encrypting all data everywhere. The z14 included some interesting new features, including “Message-Security-Assist Extension 7”. Before the z14, the PERFORM RANDOM NUMBER OPERATION (PRNO) instruction could be used to generate pseudo-random sequences at a fraction of the CPU cost of doing it in software. The z14 added a true random number generator option for PRNO.
Although high level languages like COBOL and C can’t directly use this feature, it can be used indirectly. For example, from the z14, /dev/random and /dev/urandom now use this feature: they no longer need ICSF.
Which Random Number Method to Use
So, there’s probably more ways of generating a random number on z/OS than you thought. But which should you use? There are two main factors to consider:
- Security. Most users will be satisfied with the pseudo-random numbers offered by languages like COBOL and C. Most of these use fullword (31-bit) arithmetic: a lot of possible numbers. C functions like lrand48() and drand48() use 48-bits: more possible numbers, so perhaps a little more secure. Those needing true random numbers will look towards the ICSF functions, , and the PRNO instruction: these return as many bytes of random data as you need.
- Performance and CPU usage. Traditionally, generating pseudo-random numbers has been faster than true random numbers. So, COBOL’s RANDOM statement is usually much faster than ICSF CSNBRNG. However, IBM Z hardware features have made true random number generation much faster: particularly those introduced with the z14. So, today using /dev/random is about as fast as pseudo-random number functions and features.