forked from standardml/cmlib
-
Notifications
You must be signed in to change notification settings - Fork 0
/
fortuna.sml
executable file
·95 lines (73 loc) · 2.79 KB
/
fortuna.sml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
functor FortunaFun (structure Random : RANDOM where type seed = Bytestring.string)
:>
FORTUNA
=
struct
val poolCount = 32
val minReseedTime : LargeInt.int = 100 (* milliseconds *)
(* Require 256 bits of entropy to reseed, and assume each event provides 4 bits. *)
val minPoolEvents = 64
val null : Word8.word Stream.stream = Stream.eager Stream.Nil
val pools = Array.array (poolCount, (SHA256.initial, 0)) (* each pool contains (state, # of events) *)
val lastReseed : LargeInt.int ref = ref 0
val reseedCycle : LargeInt.int ref = ref 1
fun addEntropy (pool, str) =
if pool < 0 orelse pool >= poolCount then
raise Domain
else
let
val (st, events) = Array.sub (pools, pool)
in
Array.update (pools, pool, (SHA256.update (st, str), events+1))
end
fun tryReseed () =
let
val time = Time.toMilliseconds (Time.now ())
in
if time >= !lastReseed + minReseedTime then
let
val cycle = !reseedCycle
(* p = 2^i *)
fun loop acc i p =
if i >= poolCount then
SHA256.finish (acc, null)
else
if cycle mod p = 0 then
let
val (st, events) = Array.sub (pools, i)
in
if events >= minPoolEvents then
let
val str = SHA256.finish (st, null)
in
Array.update (pools, i, (SHA256.initial, 0));
loop (SHA256.update (acc, str)) (i+1) (p*2)
end
else
loop acc (i+1) (p*2)
end
else
loop acc (i+1) (p*2)
val entropy = loop SHA256.initial 0 1
in
lastReseed := time;
reseedCycle := cycle + 1;
Random.reseed entropy
end
else
()
end
fun random n =
(
if #2 (Array.sub (pools, 0)) >= minPoolEvents then
tryReseed ()
else
();
Random.random n
)
fun initialSeed seed = Random.reseed seed
type seed = Bytestring.string
(* We'll treat a reseed as as addEntropy to pool 0. *)
fun reseed str = addEntropy (0, str)
end
structure AESFortuna = FortunaFun (structure Random = AESRandom)