/// <summary> /// HKDF-Expand-Label(Secret, Label, Context, Length) = /// HKDF-Expand(Secret, HkdfLabel, Length) /// /// The Hash function used by Transcript-Hash and HKDF is the cipher /// suite hash algorithm. Hash.length is its output length in bytes. /// Messages is the concatenation of the indicated handshake messages, /// including the handshake message type and length fields, but not /// including record layer headers. Note that in some cases a zero- /// length Context (indicated by "") is passed to HKDF-Expand-Label. The /// labels specified in this document are all ASCII strings and do not /// include a trailing NUL byte. /// </summary> /// <param name="secret">The secret.</param> /// <param name="label">The label of the intermediate value.</param> /// <param name="context">The context of the KDF step.</param> /// <param name="hashLengthBytes">Hash output length in bytes.</param> /// <returns>HKDF expanded bitstring based on the secret and built other info.</returns> public BitString ExpandLabel(BitString secret, string label, BitString context, int hashLengthBytes) { const int maxLabelContextSizes = 255; var expandedLabel = Encoding.ASCII.GetBytes("tls13 " + label); if (label.Length > maxLabelContextSizes) { throw new ArgumentOutOfRangeException($"{nameof(label)} exceeds max labels length of {maxLabelContextSizes}"); } if (context.BitLength.CeilingDivide(maxLabelContextSizes) > maxLabelContextSizes) { throw new ArgumentOutOfRangeException($"{nameof(context)} exceeds max labels length of {maxLabelContextSizes}"); } var otherInfo = BitString.Empty() .ConcatenateBits(BitString.To16BitString((short)hashLengthBytes)) .ConcatenateBits(BitString.To8BitString((byte)expandedLabel.Length)) .ConcatenateBits(new BitString(expandedLabel)) .ConcatenateBits(BitString.To8BitString((byte)(context.BitLength / BitString.BITSINBYTE))) .ConcatenateBits(context); return(_hkdf.Expand(secret, otherInfo, hashLengthBytes)); }