Initial commit.
[BoarSSL] / Crypto / HMAC_DRBG.cs
1 /*
2 * Copyright (c) 2017 Thomas Pornin <pornin@bolet.org>
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining
5 * a copy of this software and associated documentation files (the
6 * "Software"), to deal in the Software without restriction, including
7 * without limitation the rights to use, copy, modify, merge, publish,
8 * distribute, sublicense, and/or sell copies of the Software, and to
9 * permit persons to whom the Software is furnished to do so, subject to
10 * the following conditions:
11 *
12 * The above copyright notice and this permission notice shall be
13 * included in all copies or substantial portions of the Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
19 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
20 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
21 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 * SOFTWARE.
23 */
24
25 using System;
26
27 namespace Crypto {
28
29 /*
30 * Implementation of HMAC_DRBG (NIST SP800-90A).
31 *
32 * This class provides HMAC_DRBG as a deterministic PRNG from a given
33 * seed. Once a seed is set, chunks of data are obtained with
34 * GetBytes(). The GetBytes() methods can be called several times;
35 * the internal state is updated after each call. Setting a new seed
36 * resets the internal state.
37 */
38
39 public sealed class HMAC_DRBG {
40
41 HMAC hm;
42 byte[] K, V;
43 bool seeded;
44
45 /*
46 * Create the instance over the provided hash function
47 * implementation. The digest instance is linked in and will
48 * be used repeatedly. The engine is not seeded yet.
49 */
50 public HMAC_DRBG(IDigest h)
51 {
52 hm = new HMAC(h.Dup());
53 int len = h.DigestSize;
54 K = new byte[len];
55 V = new byte[len];
56 seeded = false;
57 Reset();
58 }
59
60 /*
61 * Reset the engine. A seed will have to be provided before
62 * generating pseudorandom bytes.
63 */
64 public void Reset()
65 {
66 for (int i = 0; i < K.Length; i ++) {
67 K[i] = 0x00;
68 V[i] = 0x01;
69 }
70 hm.SetKey(K);
71 }
72
73 /*
74 * Reset the engine with the provided seed.
75 */
76 public void SetSeed(byte[] seed)
77 {
78 Reset();
79 Update(seed);
80 }
81
82 /*
83 * Reset the engine with the provided seed.
84 */
85 public void SetSeed(byte[] seed, int off, int len)
86 {
87 Reset();
88 Update(seed, off, len);
89 }
90
91 /*
92 * Inject an additional seed. This may be null, in which case
93 * the state is modified but the engine is not marked as "seeded"
94 * (if it was not already marked so).
95 */
96 public void Update(byte[] seed)
97 {
98 if (seed != null) {
99 Update(seed, 0, seed.Length);
100 } else {
101 Update(null, 0, 0);
102 }
103 }
104
105 /*
106 * Inject an additional seed. If the seed length is 0, then the
107 * state is modified, but the engine is not marked as "seeded"
108 * (if it was not already marked so).
109 */
110 public void Update(byte[] seed, int off, int len)
111 {
112 /* K = HMAC_K(V || 0x00 || seed) */
113 hm.Update(V);
114 hm.Update((byte)0x00);
115 hm.Update(seed, off, len);
116 hm.DoFinal(K, 0);
117 hm.SetKey(K);
118
119 /* V = HMAC_K(V) */
120 hm.Update(V);
121 hm.DoFinal(V, 0);
122
123 /*
124 * Stop there if the additional seed is empty.
125 */
126 if (len == 0) {
127 return;
128 }
129
130 /* K = HMAC_K(V || 0x01 || seed) */
131 hm.Update(V);
132 hm.Update((byte)0x01);
133 hm.Update(seed, off, len);
134 hm.DoFinal(K, 0);
135 hm.SetKey(K);
136
137 /* V = HMAC_K(V) */
138 hm.Update(V);
139 hm.DoFinal(V, 0);
140
141 /*
142 * We get there only if a non-empty seed is used.
143 */
144 seeded = true;
145 }
146
147 /*
148 * Generate some pseudorandom bytes. The engine MUST have been
149 * seeded.
150 */
151 public void GetBytes(byte[] buf)
152 {
153 GetBytes(buf, 0, buf.Length);
154 }
155
156 /*
157 * Generate some pseudorandom bytes. The engine MUST have been
158 * seeded.
159 */
160 public void GetBytes(byte[] buf, int off, int len)
161 {
162 if (!seeded) {
163 throw new CryptoException(
164 "HMAC_DRBG engine was not seeded");
165 }
166 while (len > 0) {
167 /* V = HMAC_K(V) */
168 hm.Update(V);
169 hm.DoFinal(V, 0);
170 int clen = Math.Min(V.Length, len);
171 Array.Copy(V, 0, buf, off, clen);
172 off += clen;
173 len -= clen;
174 }
175
176 /* K = HMAC_K(V || 0x00) */
177 hm.Update(V);
178 hm.Update((byte)0x00);
179 hm.DoFinal(K, 0);
180 hm.SetKey(K);
181
182 /* V = HMAC_K(V) */
183 hm.Update(V);
184 hm.DoFinal(V, 0);
185 }
186 }
187
188 }