Initial commit.
[BoarSSL] / SSLTLS / PRF.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 using System.Text;
27
28 using Crypto;
29
30 namespace SSLTLS {
31
32 /*
33 * Implementation of the TLS PRF function. This class implements both the
34 * PRF for TLS 1.0 and 1.1 (based on MD5 and SHA-1), and the PRF for
35 * TLS 1.2 (based on a provided hash function).
36 */
37
38 public sealed class PRF {
39
40 public static byte[] LABEL_MASTER_SECRET =
41 Encoding.UTF8.GetBytes("master secret");
42 public static byte[] LABEL_KEY_EXPANSION =
43 Encoding.UTF8.GetBytes("key expansion");
44 public static byte[] LABEL_CLIENT_FINISHED =
45 Encoding.UTF8.GetBytes("client finished");
46 public static byte[] LABEL_SERVER_FINISHED =
47 Encoding.UTF8.GetBytes("server finished");
48
49 HMAC hm1, hm2;
50 byte[] bufa1, bufa2;
51 byte[] bufb1, bufb2;
52
53 /*
54 * Create a PRF instance, using both MD5 and SHA-1 (for TLS 1.0
55 * and TLS 1.1).
56 */
57 public PRF() : this(new MD5(), new SHA1())
58 {
59 }
60
61 /*
62 * Create a PRF instance, using the provided hash function (for
63 * TLS 1.2). The 'h' instance will be used internally.
64 */
65 public PRF(IDigest h) : this(h, null)
66 {
67 }
68
69 /*
70 * Get the "natural" output length; this is the output size,
71 * or sum of output sizes, of the underlying hash function(s).
72 */
73 public int NaturalOutputSize {
74 get {
75 int len = hm1.MACSize;
76 if (hm2 != null) {
77 len += hm2.MACSize;
78 }
79 return len;
80 }
81 }
82
83 PRF(IDigest h1, IDigest h2)
84 {
85 hm1 = new HMAC(h1);
86 bufa1 = new byte[hm1.MACSize];
87 bufb1 = new byte[hm1.MACSize];
88 if (h2 == null) {
89 hm2 = null;
90 bufa2 = null;
91 bufb2 = null;
92 } else {
93 hm2 = new HMAC(h2);
94 bufa2 = new byte[hm2.MACSize];
95 bufb2 = new byte[hm2.MACSize];
96 }
97 }
98
99 /*
100 * Compute the PRF, result in outBuf[].
101 */
102 public void GetBytes(byte[] secret, byte[] label, byte[] seed,
103 byte[] outBuf)
104 {
105 GetBytes(secret, label, seed, outBuf, 0, outBuf.Length);
106 }
107
108 /*
109 * Compute the PRF, result in outBuf[] (at offset 'off', producing
110 * exactly 'len' bytes).
111 */
112 public void GetBytes(byte[] secret, byte[] label, byte[] seed,
113 byte[] outBuf, int off, int len)
114 {
115 for (int i = 0; i < len; i ++) {
116 outBuf[off + i] = 0;
117 }
118 if (hm2 == null) {
119 Phash(hm1, secret, 0, secret.Length,
120 bufa1, bufb1,
121 label, seed, outBuf, off, len);
122 } else {
123 int n = (secret.Length + 1) >> 1;
124 Phash(hm1, secret, 0, n,
125 bufa1, bufb1,
126 label, seed, outBuf, off, len);
127 Phash(hm2, secret, secret.Length - n, n,
128 bufa2, bufb2,
129 label, seed, outBuf, off, len);
130 }
131 }
132
133 /*
134 * Compute the PRF, result is written in a newly allocated
135 * array (of length 'outLen' bytes).
136 */
137 public byte[] GetBytes(byte[] secret, byte[] label, byte[] seed,
138 int outLen)
139 {
140 byte[] r = new byte[outLen];
141 GetBytes(secret, label, seed, r, 0, outLen);
142 return r;
143 }
144
145 /*
146 * This function computes Phash with the specified HMAC
147 * engine, XORing the output with the current contents of
148 * the outBuf[] buffer.
149 */
150 static void Phash(HMAC hm, byte[] s, int soff, int slen,
151 byte[] bufa, byte[] bufb,
152 byte[] label, byte[] seed,
153 byte[] outBuf, int outOff, int outLen)
154 {
155 /*
156 * Set the key for HMAC.
157 */
158 hm.SetKey(s, soff, slen);
159
160 /*
161 * Compute A(1) = HMAC(secret, seed).
162 */
163 hm.Update(label);
164 hm.Update(seed);
165 hm.DoFinal(bufa, 0);
166 while (outLen > 0) {
167 /*
168 * Next chunk: HMAC(secret, A(i) + label + seed)
169 */
170 hm.Update(bufa);
171 hm.Update(label);
172 hm.Update(seed);
173 hm.DoFinal(bufb, 0);
174 int clen = Math.Min(hm.MACSize, outLen);
175 for (int i = 0; i < clen; i ++) {
176 outBuf[outOff ++] ^= bufb[i];
177 }
178 outLen -= clen;
179
180 /*
181 * If we are not finished, then compute:
182 * A(i+1) = HMAC(secret, A(i))
183 */
184 if (outLen > 0) {
185 hm.Update(bufa);
186 hm.DoFinal(bufa, 0);
187 }
188 }
189 }
190 }
191
192 }