2 * Copyright (c) 2017 Thomas Pornin <pornin@bolet.org>
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:
12 * The above copyright notice and this permission notice shall be
13 * included in all copies or substantial portions of the Software.
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
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).
38 public sealed class PRF {
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");
54 * Create a PRF instance, using both MD5 and SHA-1 (for TLS 1.0
57 public PRF() : this(new MD5(), new SHA1())
62 * Create a PRF instance, using the provided hash function (for
63 * TLS 1.2). The 'h' instance will be used internally.
65 public PRF(IDigest h) : this(h, null)
70 * Get the "natural" output length; this is the output size,
71 * or sum of output sizes, of the underlying hash function(s).
73 public int NaturalOutputSize {
75 int len = hm1.MACSize;
83 PRF(IDigest h1, IDigest h2)
86 bufa1 = new byte[hm1.MACSize];
87 bufb1 = new byte[hm1.MACSize];
94 bufa2 = new byte[hm2.MACSize];
95 bufb2 = new byte[hm2.MACSize];
100 * Compute the PRF, result in outBuf[].
102 public void GetBytes(byte[] secret, byte[] label, byte[] seed,
105 GetBytes(secret, label, seed, outBuf, 0, outBuf.Length);
109 * Compute the PRF, result in outBuf[] (at offset 'off', producing
110 * exactly 'len' bytes).
112 public void GetBytes(byte[] secret, byte[] label, byte[] seed,
113 byte[] outBuf, int off, int len)
115 for (int i = 0; i < len; i ++) {
119 Phash(hm1, secret, 0, secret.Length,
121 label, seed, outBuf, off, len);
123 int n = (secret.Length + 1) >> 1;
124 Phash(hm1, secret, 0, n,
126 label, seed, outBuf, off, len);
127 Phash(hm2, secret, secret.Length - n, n,
129 label, seed, outBuf, off, len);
134 * Compute the PRF, result is written in a newly allocated
135 * array (of length 'outLen' bytes).
137 public byte[] GetBytes(byte[] secret, byte[] label, byte[] seed,
140 byte[] r = new byte[outLen];
141 GetBytes(secret, label, seed, r, 0, outLen);
146 * This function computes Phash with the specified HMAC
147 * engine, XORing the output with the current contents of
148 * the outBuf[] buffer.
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)
156 * Set the key for HMAC.
158 hm.SetKey(s, soff, slen);
161 * Compute A(1) = HMAC(secret, seed).
168 * Next chunk: HMAC(secret, A(i) + label + seed)
174 int clen = Math.Min(hm.MACSize, outLen);
175 for (int i = 0; i < clen; i ++) {
176 outBuf[outOff ++] ^= bufb[i];
181 * If we are not finished, then compute:
182 * A(i+1) = HMAC(secret, A(i))