BcTlsSecret
BC light-weight support class for handling TLS secrets and deriving key material and other secrets
from them.
using Org.BouncyCastle.Crypto;
using Org.BouncyCastle.Crypto.Macs;
using Org.BouncyCastle.Crypto.Parameters;
using Org.BouncyCastle.Utilities;
using System;
namespace Org.BouncyCastle.Tls.Crypto.Impl.BC
{
public class BcTlsSecret : AbstractTlsSecret
{
private static readonly byte[] Ssl3Const = GenerateSsl3Constants();
protected readonly BcTlsCrypto m_crypto;
protected override AbstractTlsCrypto Crypto => m_crypto;
public static BcTlsSecret Convert(BcTlsCrypto crypto, TlsSecret secret)
{
BcTlsSecret bcTlsSecret = secret as BcTlsSecret;
if (bcTlsSecret != null)
return bcTlsSecret;
AbstractTlsSecret abstractTlsSecret = secret as AbstractTlsSecret;
if (abstractTlsSecret != null)
return crypto.AdoptLocalSecret(AbstractTlsSecret.CopyData(abstractTlsSecret));
throw new ArgumentException("unrecognized TlsSecret - cannot copy data: " + secret.GetType().FullName);
}
private static byte[] GenerateSsl3Constants()
{
int num = 15;
byte[] array = new byte[num * (num + 1) / 2];
int num2 = 0;
for (int i = 0; i < num; i++) {
byte b = (byte)(65 + i);
for (int j = 0; j <= i; j++) {
array[num2++] = b;
}
}
return array;
}
public BcTlsSecret(BcTlsCrypto crypto, byte[] data)
: base(data)
{
m_crypto = crypto;
}
public override TlsSecret DeriveUsingPrf(int prfAlgorithm, string label, byte[] seed, int length)
{
lock (this) {
CheckAlive();
switch (prfAlgorithm) {
case 4:
return TlsCryptoUtilities.HkdfExpandLabel(this, 4, label, seed, length);
case 5:
return TlsCryptoUtilities.HkdfExpandLabel(this, 5, label, seed, length);
case 7:
return TlsCryptoUtilities.HkdfExpandLabel(this, 7, label, seed, length);
default:
return m_crypto.AdoptLocalSecret(Prf(prfAlgorithm, label, seed, length));
}
}
}
public override TlsSecret DeriveUsingPrf(int prfAlgorithm, ReadOnlySpan<char> label, ReadOnlySpan<byte> seed, int length)
{
lock (this) {
CheckAlive();
switch (prfAlgorithm) {
case 4:
return TlsCryptoUtilities.HkdfExpandLabel(this, 4, label, seed, length);
case 5:
return TlsCryptoUtilities.HkdfExpandLabel(this, 5, label, seed, length);
case 7:
return TlsCryptoUtilities.HkdfExpandLabel(this, 7, label, seed, length);
default:
return m_crypto.AdoptLocalSecret(Prf(prfAlgorithm, label, seed, length));
}
}
}
public override TlsSecret HkdfExpand(int cryptoHashAlgorithm, byte[] info, int length)
{
return HkdfExpand(cryptoHashAlgorithm, info.AsSpan(), length);
}
public unsafe override TlsSecret HkdfExpand(int cryptoHashAlgorithm, ReadOnlySpan<byte> info, int length)
{
if (length < 1)
return m_crypto.AdoptLocalSecret(TlsUtilities.EmptyBytes);
int hashOutputSize = TlsCryptoUtilities.GetHashOutputSize(cryptoHashAlgorithm);
if (length > 255 * hashOutputSize)
throw new ArgumentException("must be <= 255 * (output size of 'hashAlgorithm')", "length");
HMac hMac = new HMac(m_crypto.CreateDigest(cryptoHashAlgorithm));
lock (this) {
CheckAlive();
ReadOnlySpan<byte> key = m_data;
hMac.Init(new KeyParameter(key));
}
byte[] array = new byte[length];
Span<byte> span;
if (hashOutputSize <= 128) {
int num = hashOutputSize;
span = new Span<byte>(stackalloc byte[(int)(uint)num], num);
} else
span = new byte[hashOutputSize];
Span<byte> span2 = span;
byte b = 0;
int num2 = 0;
int num3;
while (true) {
hMac.BlockUpdate(info);
hMac.Update(b = (byte)(b + 1));
hMac.DoFinal(span2);
num3 = length - num2;
if (num3 <= hashOutputSize)
break;
span2.CopyTo(array.AsSpan(num2));
num2 += hashOutputSize;
hMac.BlockUpdate(span2);
}
span = span2.Slice(0, num3);
span.CopyTo(array.AsSpan(num2));
return m_crypto.AdoptLocalSecret(array);
}
public override TlsSecret HkdfExtract(int cryptoHashAlgorithm, TlsSecret ikm)
{
byte[] key = Extract();
HMac hMac = new HMac(m_crypto.CreateDigest(cryptoHashAlgorithm));
hMac.Init(new KeyParameter(key));
Convert(m_crypto, ikm).UpdateMac(hMac);
byte[] array = new byte[hMac.GetMacSize()];
hMac.DoFinal(array, 0);
return m_crypto.AdoptLocalSecret(array);
}
protected virtual void HmacHash(int cryptoHashAlgorithm, byte[] secret, int secretOff, int secretLen, byte[] seed, byte[] output)
{
HMac hMac = new HMac(m_crypto.CreateDigest(cryptoHashAlgorithm));
hMac.Init(new KeyParameter(secret, secretOff, secretLen));
byte[] array = seed;
int macSize = hMac.GetMacSize();
byte[] array2 = new byte[macSize];
byte[] array3 = new byte[macSize];
for (int i = 0; i < output.Length; i += macSize) {
hMac.BlockUpdate(array, 0, array.Length);
hMac.DoFinal(array2, 0);
array = array2;
hMac.BlockUpdate(array, 0, array.Length);
hMac.BlockUpdate(seed, 0, seed.Length);
hMac.DoFinal(array3, 0);
Array.Copy(array3, 0, output, i, System.Math.Min(macSize, output.Length - i));
}
}
protected virtual byte[] Prf(int prfAlgorithm, string label, byte[] seed, int length)
{
if (prfAlgorithm == 0)
return Prf_Ssl(seed, length);
byte[] labelSeed = Arrays.Concatenate(Strings.ToByteArray(label), seed);
if (1 == prfAlgorithm)
return Prf_1_0(labelSeed, length);
return Prf_1_2(prfAlgorithm, labelSeed, length);
}
protected virtual byte[] Prf(int prfAlgorithm, ReadOnlySpan<char> label, ReadOnlySpan<byte> seed, int length)
{
if (prfAlgorithm == 0)
return Prf_Ssl(seed, length);
byte[] array = new byte[label.Length + seed.Length];
for (int i = 0; i < label.Length; i++) {
array[i] = (byte)label[i];
}
seed.CopyTo(array.AsSpan(label.Length));
if (1 == prfAlgorithm)
return Prf_1_0(array, length);
return Prf_1_2(prfAlgorithm, array, length);
}
protected virtual byte[] Prf_Ssl(byte[] seed, int length)
{
return Prf_Ssl(seed.AsSpan(), length);
}
protected unsafe virtual byte[] Prf_Ssl(ReadOnlySpan<byte> seed, int length)
{
IDigest digest = m_crypto.CreateDigest(1);
IDigest digest2 = m_crypto.CreateDigest(2);
int digestSize = digest.GetDigestSize();
int digestSize2 = digest2.GetDigestSize();
int num = System.Math.Max(digestSize, digestSize2);
Span<byte> output = new Span<byte>(stackalloc byte[(int)(uint)num], num);
byte[] array = new byte[length];
int num2 = 1;
int num3 = 0;
int num4 = 0;
while (num4 < length) {
digest2.BlockUpdate(Ssl3Const.AsSpan(num3, num2));
num3 += num2++;
digest2.BlockUpdate(m_data);
digest2.BlockUpdate(seed);
digest2.DoFinal(output);
digest.BlockUpdate(m_data);
digest.BlockUpdate(output.Slice(0, digestSize2));
int num6 = length - num4;
if (num6 < digestSize) {
digest.DoFinal(output);
output.Slice(0, num6).CopyTo(array.AsSpan(num4));
num4 += num6;
} else {
digest.DoFinal(array.AsSpan(num4));
num4 += digestSize;
}
}
return array;
}
protected virtual byte[] Prf_1_0(byte[] labelSeed, int length)
{
int num = (m_data.Length + 1) / 2;
byte[] array = new byte[length];
HmacHash(1, m_data, 0, num, labelSeed, array);
byte[] array2 = new byte[length];
HmacHash(2, m_data, m_data.Length - num, num, labelSeed, array2);
for (int i = 0; i < length; i++) {
array[i] ^= array2[i];
}
return array;
}
protected virtual byte[] Prf_1_2(int prfAlgorithm, byte[] labelSeed, int length)
{
int hashForPrf = TlsCryptoUtilities.GetHashForPrf(prfAlgorithm);
byte[] array = new byte[length];
HmacHash(hashForPrf, m_data, 0, m_data.Length, labelSeed, array);
return array;
}
protected virtual void UpdateMac(IMac mac)
{
lock (this) {
CheckAlive();
mac.BlockUpdate(m_data, 0, m_data.Length);
}
}
}
}