<PackageReference Include="BouncyCastle.Cryptography" Version="2.3.0" />

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); } } } }