DBA Data[Home] [Help]

PACKAGE BODY: APPS.FND_RANDOM_NUMBER

Source


1 PACKAGE body FND_RANDOM_NUMBER AS
2 /* $Header: AFSCRNGB.pls 120.2 2005/07/02 03:09:22 appldev noship $ */
3 
4 --
5 -- Tuneable constants:
6 --
7 -- C_BLOCK_SIZE  The number of random values that can be generated off
8 --               of the cached value for the counter before we need to
9 --               go back to the table and sequence again.  This number
10 --               times the C_REKEY_SIZE must be less than 2^20.  This
11 --               must be greater than 2000/8 to satisfy the block get API.
12 -- C_REKEY_SIZE  The number of random blocks that can be generated off
13 --               of a key before a recomputation of the key is required.
14 --               When a process goes to the table/sequence and detects
15 --               that this number of blocks has been used, that process
16 --               is responsible for recomputing the key and updating the
17 --               the table with the new values.
18 -- C_FLUSH_SIZE  The number of random events that can be buffered in
19 --               the pools before the code must flush the entropy to
20 --               one-row table.  This must be a multiple of 32 so that
21 --               table updates don't favor earlier pools with more
22 --               of the available entropy.
23 -- C_EVENT_SIZE  The number of random events that must be collected in
24 --               the table before a reseeding operation can be performed.
25 --
26   C_BLOCK_SIZE   constant number       := 4096;
27   C_REKEY_SIZE   constant number       := 64;
28   C_FLUSH_SIZE   constant number       := 1024;
29   C_EVENT_SIZE   constant number       := 65536;
30 --
31 -- Static (class) variables, do not change
32 --
33   S_HEX_FORMAT   constant varchar2(60) := 'FM0XXXXXXXXXXXXXXX';
34   S_MAX_COUNTER  constant number       := power(2, 64);
35 --
36 -- Table of MD5 values (raws)
37 --
38   type RAWTAB is table of raw(16) index by binary_integer;
39 --
40 -- Writable class member variables
41 --
42   M_NUM_RANDOMS   number := 0;
43   M_COUNTER       number := 0;
44   M_KEY           raw(24);
45   M_POOLS         RAWTAB;
46   M_EVENTS        number := 0;
47 
48 --
49 -- Internal function - generates an entropy raw based on the SYS_GUID,
50 -- SYSDATE, session ID, DBMS_RANDOM, and any other input entropy.
51 -- Concatenates this with an existing entropy value (if any).
52 --
53   function GENERATE_ENTROPY(P_ENTROPY in raw default null,
54                             P_OLDVALUE in raw default null)
55   return raw
56   is
57     X_E1    raw(16);
58     X_E2    raw(16);
59     X_E3    raw(16);
60     X_E4    raw(16);
61   begin
62     -- select SYS_GUID() into X_E1 from dual;
63     -- replacing with the line below for bug 4082303
64     X_E1 := SYS_GUID();
65     X_E2 := DBMS_OBFUSCATION_TOOLKIT.MD5(INPUT =>
66               UTL_RAW.CAST_TO_RAW(to_char(SYSDATE, 'YYYYMMDD HH24MISS')||' '||
67                                   USERENV('SESSIONID')));
68     X_E3 := hextoraw(to_char(DBMS_RANDOM.RANDOM+power(2,31),'FM0XXXXXXX')||
69                      to_char(DBMS_RANDOM.RANDOM+power(2,31),'FM0XXXXXXX')||
70                      to_char(DBMS_RANDOM.RANDOM+power(2,31),'FM0XXXXXXX')||
71                      to_char(DBMS_RANDOM.RANDOM+power(2,31),'FM0XXXXXXX'));
72     if (P_ENTROPY is not null) then
73       X_E4 := DBMS_OBFUSCATION_TOOLKIT.MD5(INPUT => P_ENTROPY);
74     else
75       X_E4 := hextoraw('0123456789ABCDEF0123456789ABCDEF');
76     end if;
77     return(UTL_RAW.CONCAT(X_E1, X_E2, X_E3, X_E4, P_OLDVALUE));
78   end GENERATE_ENTROPY;
79 
80 --
81 -- Add some entropy to the in-memory pools.  Entropy is added in
82 -- round-robin fashion to 32 pools.  Each call to this routine is
83 -- considered one event.  Even if no entropy is passed in to this
84 -- routine, it will attempt to get some by using the GUID mechanism,
85 -- the SYSDATE and SESSIONID (together), and the current state of
86 -- DBMS_RANDOM.  The entropy is added to the next pool in sequence.
87 -- Only the resulting MD5 is stored - there is no buffering of
88 -- intermediate amounts of entropy (seems pointless because we're
89 -- not getting much entropy from the above sources anyway).
90 --
91   procedure ADD_ENTROPY(P_ENTROPY in raw default null)
92   is
93     X_INDEX number;
94   begin
95     if (M_EVENTS = 0) then
96       for M_EVENTS in 1..32 loop
97         M_POOLS(M_EVENTS) := null;
98       end loop;
99       M_EVENTS := 0;
100     end if;
101     X_INDEX := mod(M_EVENTS, 32) + 1;
102     M_EVENTS := M_EVENTS + 1;
103     M_POOLS(X_INDEX) := DBMS_OBFUSCATION_TOOLKIT.MD5(INPUT =>
104                           UTL_RAW.CONCAT(GENERATE_ENTROPY(P_ENTROPY,
105                                                           M_POOLS(X_INDEX))));
106     if (M_EVENTS >= C_FLUSH_SIZE) then
107       FLUSH_ENTROPY;
108     end if;
109   end ADD_ENTROPY;
110 --
111 -- Write any accumulated entropy to the table in an autonomous transaction.
112 -- The entropy collected is assumed to be evenly distributed across the 32
113 -- pools.  If enough entropy has been accumulated in the table, a reseeding
114 -- of the generator is done.
115 --
116   procedure FLUSH_ENTROPY
117   is
118   pragma AUTONOMOUS_TRANSACTION;
119     X_COUNT   number;
120     X_EVENTS  number;
121     X_POOL    RAWTAB;
122   begin
123     if (M_EVENTS = 0) then
124       return; -- No entropy available yet
125     end if;
126     -- Initialize the pool arrays
127     for X_BIT in 1..32 loop
128       X_POOL(X_BIT) := null;
129     end loop;
130     -- Get the current state of the entropy pools from disk
131     select EVENT_COUNT,
132            POOL0,  POOL1,  POOL2,  POOL3,  POOL4,  POOL5,  POOL6,  POOL7,
133            POOL8,  POOL9,  POOL10, POOL11, POOL12, POOL13, POOL14, POOL15,
134            POOL16, POOL17, POOL18, POOL19, POOL20, POOL21, POOL22, POOL23,
135            POOL24, POOL25, POOL26, POOL27, POOL28, POOL29, POOL30, POOL31
136       into X_EVENTS,
137            X_POOL(1),  X_POOL(2),  X_POOL(3),  X_POOL(4),  X_POOL(5),
138            X_POOL(6),  X_POOL(7),  X_POOL(8),  X_POOL(9),  X_POOL(10),
139            X_POOL(11), X_POOL(12), X_POOL(13), X_POOL(14), X_POOL(15),
140            X_POOL(16), X_POOL(17), X_POOL(18), X_POOL(19), X_POOL(20),
141            X_POOL(21), X_POOL(22), X_POOL(23), X_POOL(24), X_POOL(25),
142            X_POOL(26), X_POOL(27), X_POOL(28), X_POOL(29), X_POOL(30),
143            X_POOL(31), X_POOL(32)
144       from FND_RAND_STATES
145      where LOCK_ID = 1
146        for update;
147     -- Merge as much entropy as available into the pools
148     for X_COUNT in 1..32 loop
149       if (M_POOLS(X_COUNT) is not null) then
150         X_POOL(X_COUNT) := DBMS_OBFUSCATION_TOOLKIT.MD5(INPUT =>
151                              UTL_RAW.CONCAT(X_POOL(X_COUNT),M_POOLS(X_COUNT)));
152         M_POOLS(X_COUNT) := null;
153       end if;
154     end loop;
155     -- Update the pools and event counter
156     X_EVENTS := X_EVENTS + M_EVENTS;
157     update FND_RAND_STATES
158        set EVENT_COUNT = X_EVENTS,
159            POOL0  = X_POOL(1),
160            POOL1  = X_POOL(2),
161            POOL2  = X_POOL(3),
162            POOL3  = X_POOL(4),
163            POOL4  = X_POOL(5),
164            POOL5  = X_POOL(6),
165            POOL6  = X_POOL(7),
166            POOL7  = X_POOL(8),
167            POOL8  = X_POOL(9),
168            POOL9  = X_POOL(10),
169            POOL10 = X_POOL(11),
170            POOL11 = X_POOL(12),
171            POOL12 = X_POOL(13),
172            POOL13 = X_POOL(14),
173            POOL14 = X_POOL(15),
174            POOL15 = X_POOL(16),
175            POOL16 = X_POOL(17),
176            POOL17 = X_POOL(18),
177            POOL18 = X_POOL(19),
178            POOL19 = X_POOL(20),
179            POOL20 = X_POOL(21),
180            POOL21 = X_POOL(22),
181            POOL22 = X_POOL(23),
182            POOL23 = X_POOL(24),
183            POOL24 = X_POOL(25),
184            POOL25 = X_POOL(26),
185            POOL26 = X_POOL(27),
186            POOL27 = X_POOL(28),
187            POOL28 = X_POOL(29),
188            POOL29 = X_POOL(30),
189            POOL30 = X_POOL(31),
190            POOL31 = X_POOL(32)
191       where LOCK_ID = 1;
192     -- If enough events have been accumulated, reseed
193     if (X_EVENTS >= C_EVENT_SIZE) then
194       RESEED_GENERATOR;
195     end if;
196     commit;
197     -- Mark the in-memory entropy pools as empty
198     M_EVENTS := 0;
199   exception when OTHERS then
200     rollback;
201   end FLUSH_ENTROPY;
202 --
203 -- Reseed the random number generator using entropy from the table
204 -- If insufficient entropy is available, aborts the operation and
205 -- returns.  This code runs in the same transaction as the caller;
206 -- the caller is responsible for committing or rolling back the
207 -- changes.  Note that on returning, this code always leaves the
208 -- one-row table in a locked state (even if it aborts), so the caller
209 -- should always commit or rollback, preferably soon after the call.
210 --
211   procedure RESEED_GENERATOR
212   is
213     X_BIT       number;
214     X_POOL      RAWTAB;
215     X_COUNTER   number;
216     X_EVENTS    number;
217     X_SEQUENCE  number;
218     X_KEY       raw(24);
219     X_ENTROPY   raw(512);
220   begin
221     -- Initialize the pool arrays
222     for X_BIT in 1..32 loop
223       X_POOL(X_BIT) := null;
224     end loop;
225     -- Get the current state of the key, seed counter, and entropy
226     select LAST_SEQUENCE, RANDOM_KEY, RESEED_COUNTER, EVENT_COUNT,
227            POOL0,  POOL1,  POOL2,  POOL3,  POOL4,  POOL5,  POOL6,  POOL7,
228            POOL8,  POOL9,  POOL10, POOL11, POOL12, POOL13, POOL14, POOL15,
229            POOL16, POOL17, POOL18, POOL19, POOL20, POOL21, POOL22, POOL23,
230            POOL24, POOL25, POOL26, POOL27, POOL28, POOL29, POOL30, POOL31
231       into X_SEQUENCE, X_KEY, X_COUNTER, X_EVENTS,
232            X_POOL(1),  X_POOL(2),  X_POOL(3),  X_POOL(4),  X_POOL(5),
233            X_POOL(6),  X_POOL(7),  X_POOL(8),  X_POOL(9),  X_POOL(10),
234            X_POOL(11), X_POOL(12), X_POOL(13), X_POOL(14), X_POOL(15),
235            X_POOL(16), X_POOL(17), X_POOL(18), X_POOL(19), X_POOL(20),
236            X_POOL(21), X_POOL(22), X_POOL(23), X_POOL(24), X_POOL(25),
237            X_POOL(26), X_POOL(27), X_POOL(28), X_POOL(29), X_POOL(30),
238            X_POOL(31), X_POOL(32)
239       from FND_RAND_STATES
240      where LOCK_ID = 1
241        for update;
242     -- Draw entropy from the pools
243     X_ENTROPY := null;
244     for X_BIT in 0..31 loop
245       if (mod(X_COUNTER, power(2, X_BIT)) = 0) then
246         -- Use this pool for the reseed operation
247         if (X_POOL(X_BIT + 1) is not null) then
248           X_ENTROPY := UTL_RAW.CONCAT(X_ENTROPY, X_POOL(X_BIT + 1));
249           X_POOL(X_BIT + 1) := null;
250         end if;
251       end if;
252     end loop;
253     if (X_ENTROPY is null) then
254       -- Insufficient entropy available, abort the reseeding
255       return;
256     end if;
257     -- Recompute the key by taking the MD5 of the old key plus the entropy.
258     X_KEY := UTL_RAW.CONCAT(
259                UTL_RAW.SUBSTR(DBMS_OBFUSCATION_TOOLKIT.MD5(INPUT =>
260                  UTL_RAW.CONCAT(hextoraw('1'), X_KEY, X_ENTROPY)), 1, 12),
261                UTL_RAW.SUBSTR(DBMS_OBFUSCATION_TOOLKIT.MD5(INPUT =>
262                  UTL_RAW.CONCAT(hextoraw('2'), X_KEY, X_ENTROPY)), 1, 12));
263     -- Increment the seed counter
264     if (X_COUNTER = power(2,31)) then
265       X_COUNTER := 1;
266     else
267       X_COUNTER := X_COUNTER + 1;
268     end if;
269     -- Update the reseed counter, key, and entropy;
270     -- set the last sequence to 0 to force a re-initialization to occur
271     update FND_RAND_STATES
272        set LAST_SEQUENCE = 0,
273            EVENT_COUNT = 0,
274            RESEED_COUNTER = X_COUNTER,
275            RANDOM_KEY = X_KEY,
276            POOL0  = X_POOL(1),
277            POOL1  = X_POOL(2),
278            POOL2  = X_POOL(3),
279            POOL3  = X_POOL(4),
280            POOL4  = X_POOL(5),
281            POOL5  = X_POOL(6),
282            POOL6  = X_POOL(7),
283            POOL7  = X_POOL(8),
284            POOL8  = X_POOL(9),
285            POOL9  = X_POOL(10),
286            POOL10 = X_POOL(11),
287            POOL11 = X_POOL(12),
288            POOL12 = X_POOL(13),
289            POOL13 = X_POOL(14),
290            POOL14 = X_POOL(15),
291            POOL15 = X_POOL(16),
292            POOL16 = X_POOL(17),
293            POOL17 = X_POOL(18),
294            POOL18 = X_POOL(19),
295            POOL19 = X_POOL(20),
296            POOL20 = X_POOL(21),
297            POOL21 = X_POOL(22),
298            POOL22 = X_POOL(23),
299            POOL23 = X_POOL(24),
300            POOL24 = X_POOL(25),
301            POOL25 = X_POOL(26),
302            POOL26 = X_POOL(27),
303            POOL27 = X_POOL(28),
304            POOL28 = X_POOL(29),
305            POOL29 = X_POOL(30),
306            POOL30 = X_POOL(31),
307            POOL31 = X_POOL(32)
308       where LOCK_ID = 1;
309   exception when OTHERS then
310     rollback;
311   end RESEED_GENERATOR;
312 
313 --
314 -- This routine updates the generator state stored in the one-row table
315 -- using an autonomous transaction.  Since the counter is coming from a
316 -- sequence, that means that this routine really just updates the key
317 -- stored in the table.  The update also stores the value of the sequence,
318 -- so that later readers of the table can determine how many blocks of
319 -- random values have been generated using the stored key.
320 --
321   procedure UPDATE_KEY(P_LASTCOUNT in number)
322   is
323   pragma AUTONOMOUS_TRANSACTION;
324     X_NEWKEY    raw(24);
325     X_R1        raw(8);
326     X_R2        raw(8);
327     X_R3        raw(8);
328     X_LASTCOUNT number;
329     X_ENTROPY   raw(512);
330   begin
331     -- Re-fetch the row to lock it for update
332     select LAST_SEQUENCE, RANDOM_KEY
333       into X_LASTCOUNT, M_KEY
334       from FND_RAND_STATES
335      where LOCK_ID = 1
336        for update;
337     -- Recheck it to make sure we need to update it; it's possible
338     -- that another process beat us to it.
339     if (X_LASTCOUNT = P_LASTCOUNT) then
340       if (P_LASTCOUNT = 0) then
341         -- This is the first process to touch the row, force a key change
342         X_ENTROPY := GENERATE_ENTROPY;
343         M_KEY := UTL_RAW.CONCAT(
344                    UTL_RAW.SUBSTR(DBMS_OBFUSCATION_TOOLKIT.MD5(INPUT =>
345                      UTL_RAW.CONCAT(hextoraw('1'), M_KEY, X_ENTROPY)), 1, 12),
346                    UTL_RAW.SUBSTR(DBMS_OBFUSCATION_TOOLKIT.MD5(INPUT =>
347                      UTL_RAW.CONCAT(hextoraw('2'), M_KEY, X_ENTROPY)), 1, 12));
348       end if;
349       -- Update is required
350       X_LASTCOUNT := M_COUNTER;
351       M_COUNTER := mod(M_COUNTER * C_BLOCK_SIZE, S_MAX_COUNTER);
352       -- Now regenerate the key
353       X_R1 := hextoraw(to_char(M_COUNTER, S_HEX_FORMAT));
354       M_COUNTER := mod(M_COUNTER + 1, S_MAX_COUNTER);
355       X_R2 := hextoraw(to_char(M_COUNTER, S_HEX_FORMAT));
356       M_COUNTER := mod(M_COUNTER + 1, S_MAX_COUNTER);
357       X_R3 := hextoraw(to_char(M_COUNTER, S_HEX_FORMAT));
358       M_COUNTER := mod(M_COUNTER + 2, S_MAX_COUNTER);
359       X_NEWKEY := DBMS_OBFUSCATION_TOOLKIT.DES3ENCRYPT(
360                     INPUT => UTL_RAW.CONCAT(X_R1, X_R2, X_R3), KEY => M_KEY);
361       -- Used the first 4 randoms from this block to regenerate the key
362       -- (one value was discarded to stay on an even count)
363       M_NUM_RANDOMS := C_BLOCK_SIZE - 4;
364       -- Update the counter and key
365       update FND_RAND_STATES
366          set LAST_SEQUENCE = X_LASTCOUNT,
367              RANDOM_KEY = X_NEWKEY
368       where LOCK_ID = 1;
369       commit;
370     else
371       -- Update was done by another process; unlock the one-row table
372       rollback;
373       M_COUNTER := mod(M_COUNTER * C_BLOCK_SIZE, S_MAX_COUNTER);
374       M_NUM_RANDOMS := C_BLOCK_SIZE;
375     end if;
376   exception when OTHERS then
377     rollback;
378   end UPDATE_KEY;
379 --
380 -- Initialize the in-memory generator by fetching the counter value from
381 -- the sequence and the key from the one-row table.  This in-memory state
382 -- is then used as needed to generate random numbers (up to a pre-defined
383 -- block size).  The fetch doesn't normally require that the one-row table
384 -- be locked (because the counter comes from a sequence).  Howver, if this
385 -- code detects that enough blocks have been generated from the current key,
386 -- it forces a recomputation of the key using UPDATE_KEY; we don't want
387 -- that to happen too often because it requires locking the table.
388 --
389   procedure INITIALIZE_GENERATOR
390   is
391     X_NEWKEY    raw(24);
392     X_LASTCOUNT number;
393   begin
394     -- Get the current state of the random counter and key
395     select LAST_SEQUENCE, RANDOM_KEY, FND_RAND_S.NEXTVAL
396       into X_LASTCOUNT, M_KEY, M_COUNTER
397       from FND_RAND_STATES
398      where LOCK_ID = 1;
399     if ((X_LASTCOUNT = 0) or (M_COUNTER < X_LASTCOUNT) or
400         ((M_COUNTER - X_LASTCOUNT) >= C_REKEY_SIZE)) then
401       -- If the sequence has advanced enough, recompute the key
402       UPDATE_KEY(X_LASTCOUNT);
403     else
404       -- Otherwise just consume the next block
405       M_COUNTER := mod(M_COUNTER * C_BLOCK_SIZE, S_MAX_COUNTER);
406       M_NUM_RANDOMS := C_BLOCK_SIZE;
407     end if;
408   end INITIALIZE_GENERATOR;
409 --
410 -- Get a random value as a 16-byte (128-bit) raw.  Values normally come
411 -- by generating them off of an in-memory cache containing the counter
412 -- and key.  This cache is used up to a pre-defined block size, after
413 -- which the cache is re-synced by calling INITIALIZE_GENERATOR.  The
414 -- first call is forced to do this, which means it's forced to read
415 -- the one-row table (but hopefully not to lock/update it).  Note that
416 -- this code runs the ADD_ENTROPY routine on every call in a pathetic
417 -- attempt to collect whatever entropy we can; this adds some cost to
418 -- the routine, and every so often will incur the additional cost of
419 -- updating the entropy in the table, or, worse, reseeding the key.
420 --
421   function GET_RANDOM return raw
422   is
423   begin
424     return(GET_RANDOM_BYTES(16));
425   end GET_RANDOM;
426 --
427   function GET_RANDOM_BYTES(P_NBYTES in number default null) return raw
428   is
429     X_NRAND number;
430     X_EXTRA number;
431     X_BYTES raw(2000);
432   begin
433     -- Reject invalid requests
434     if ((P_NBYTES is null) or (P_NBYTES < 1) or (P_NBYTES > 2000)) then
435       return(null);
436     end if;
437     -- Collect whatever entropy we can from GUID, SYSDATE, DBMS_RANDOM
438     ADD_ENTROPY;
439     -- Compute number of bytes to round up the request to 64-bit boundary
440     X_EXTRA := mod(P_NBYTES, 8);
441     if (X_EXTRA > 0) then
442       X_EXTRA := 8 - X_EXTRA;
443     end if;
444     X_NRAND := (P_NBYTES + X_EXTRA) / 8;
445     if (M_NUM_RANDOMS < X_NRAND) then
446       -- If the current block of randoms is too small, re-initialize
447       INITIALIZE_GENERATOR;
448     end if;
449     -- Set the buffer to the first counter value
450     M_NUM_RANDOMS := M_NUM_RANDOMS - X_NRAND;
451     X_BYTES := hextoraw(to_char(M_COUNTER, S_HEX_FORMAT));
452     X_NRAND := X_NRAND - 1;
453     M_COUNTER := mod(M_COUNTER + 1, S_MAX_COUNTER);
454     -- Concatenate additional counter values to fill the request
455     while (X_NRAND > 0) loop
456       X_BYTES := UTL_RAW.CONCAT(X_BYTES,
457                                 hextoraw(to_char(M_COUNTER, S_HEX_FORMAT)));
458       M_COUNTER := mod(M_COUNTER + 1, S_MAX_COUNTER);
459       X_NRAND := X_NRAND - 1;
460     end loop;
461     -- Return the encryption of the counter sequence
462     if (X_EXTRA > 0) then
463       -- If rounding was done, truncate unwanted bytes
464       return(UTL_RAW.SUBSTR(DBMS_OBFUSCATION_TOOLKIT.DES3ENCRYPT(
465                               INPUT => X_BYTES, KEY => M_KEY),
466                             1, P_NBYTES));
467     end if;
468     return(DBMS_OBFUSCATION_TOOLKIT.DES3ENCRYPT(INPUT => X_BYTES,
469                                                 KEY => M_KEY));
470   end GET_RANDOM_BYTES;
471 
472   procedure ADD_EXTERNAL_ENTROPY(P_E in raw default null)
473   is
474   pragma AUTONOMOUS_TRANSACTION;
475      X_ENTROPY   raw(512);
476      X_NEWKEY    raw(24);
477   begin
478      X_ENTROPY := GENERATE_ENTROPY(P_ENTROPY=>P_E);
479      select RANDOM_KEY
480        into X_NEWKEY
481        from FND_RAND_STATES
482      where LOCK_ID = 1
483        for update;
484      X_NEWKEY := DBMS_OBFUSCATION_TOOLKIT.MD5(
485                      INPUT => UTL_RAW.CONCAT(X_NEWKEY,X_ENTROPY));
486      X_NEWKEY := UTL_RAW.SUBSTR(
487                      UTL_RAW.CONCAT(X_NEWKEY,
488                        DBMS_OBFUSCATION_TOOLKIT.MD5(
489                          INPUT => UTL_RAW.CONCAT(X_NEWKEY, X_NEWKEY))),
490                      1, 24);
491      update FND_RAND_STATES
492        set RANDOM_KEY = X_NEWKEY,
493            LAST_SEQUENCE = 0
494      where LOCK_ID = 1;
495      commit;
496   exception when OTHERS then
497      rollback;
498   end ADD_EXTERNAL_ENTROPY;
499 
500 END FND_RANDOM_NUMBER;