using System.Text;
using System.Security.Cryptography;
namespace Hazel.Dtls
{
///
/// Common Psuedorandom Function labels for TLS
///
public struct PrfLabel
{
public static readonly ByteSpan MASTER_SECRET = LabelToBytes("master secert");
public static readonly ByteSpan KEY_EXPANSION = LabelToBytes("key expansion");
public static readonly ByteSpan CLIENT_FINISHED = LabelToBytes("client finished");
public static readonly ByteSpan SERVER_FINISHED = LabelToBytes("server finished");
///
/// Convert a text label to a byte sequence
///
public static ByteSpan LabelToBytes(string label)
{
return Encoding.ASCII.GetBytes(label);
}
}
///
/// The P_SHA256 Psuedorandom Function
///
public struct PrfSha256
{
///
/// Expand a secret key
///
/// Output span. Length determines how much data to generate
/// Original key to expand
/// Label (treated as a salt)
/// Seed for expansion (treated as a salt)
public static void ExpandSecret(ByteSpan output, ByteSpan key, string label, ByteSpan initialSeed)
{
ExpandSecret(output, key, PrfLabel.LabelToBytes(label), initialSeed);
}
///
/// Expand a secret key
///
/// Output span. Length determines how much data to generate
/// Original key to expand
/// Label (treated as a salt)
/// Seed for expansion (treated as a salt)
public static void ExpandSecret(ByteSpan output, ByteSpan key, ByteSpan label, ByteSpan initialSeed)
{
ByteSpan writer = output;
byte[] roundSeed = new byte[label.Length + initialSeed.Length];
label.CopyTo(roundSeed);
initialSeed.CopyTo(roundSeed, label.Length);
byte[] hashA = roundSeed;
using (HMACSHA256 hmac = new HMACSHA256(key.ToArray()))
{
byte[] input = new byte[hmac.OutputBlockSize + roundSeed.Length];
new ByteSpan(roundSeed).CopyTo(input, hmac.OutputBlockSize);
while (writer.Length > 0)
{
// Update hashA
hashA = hmac.ComputeHash(hashA);
// generate hash input
new ByteSpan(hashA).CopyTo(input);
ByteSpan roundOutput = hmac.ComputeHash(input);
if (roundOutput.Length > writer.Length)
{
roundOutput = roundOutput.Slice(0, writer.Length);
}
roundOutput.CopyTo(writer);
writer = writer.Slice(roundOutput.Length);
}
}
}
}
}