using System; using System.Collections.Generic; using System.Collections.Specialized; namespace S22.Sasl.Mechanisms.Srp { /// /// Represents the second client-response sent to the server as part of /// the SRP authentication exchange. /// internal class ClientMessage2 { /// /// The client's ephemeral public key. /// public Mpi PublicKey { get; set; } /// /// The evidence which proves to the server client-knowledge of the shared /// context key. /// public byte[] Proof { get; set; } /// /// The options list indicating the security services chosen by the client. /// public NameValueCollection Options { get; private set; } /// /// The initial vector the server will use to set up its encryption /// context, if confidentiality protection will be employed. /// public byte[] InitialVector { get; set; } /// /// Creates and initializes a new instance of the ClientMessage2 class. /// private ClientMessage2() { Options = new NameValueCollection(); InitialVector = new byte[0]; } /// /// Creates and initializes a new instance of the ClientMessage2 class using /// the specified public key and client proof. /// /// The client's public key. /// The calculated client proof. /// Thrown if either the public key /// or the proof parameter is null. public ClientMessage2(Mpi publicKey, byte[] proof) : this() { publicKey.ThrowIfNull("publicKey"); proof.ThrowIfNull("proof"); PublicKey = publicKey; Proof = proof; } /// /// Serializes this instance of the ClientMessage2 class into a sequence of /// bytes according to the requirements of the SRP specification. /// /// A sequence of bytes representing this instance of the /// ClientMessage2 class. /// Thrown if the cummultative length /// of the serialized data fields exceeds the maximum number of bytes /// allowed as per SRP specification. /// SRP specification imposes a limit of 2,147,483,643 bytes on /// the serialized data. public byte[] Serialize() { byte[] publicKey = PublicKey.Serialize(), M1 = new OctetSequence(Proof).Serialize(), cIV = new OctetSequence(InitialVector).Serialize(), options = new Utf8String(BuildOptionsString()).Serialize(); int length = publicKey.Length + M1.Length + cIV.Length + options.Length; return new ByteBuilder() .Append(length, true) .Append(publicKey) .Append(M1) .Append(options) .Append(cIV) .ToArray(); } /// /// Serializes the client's options collection into a comma-seperated /// options string. /// /// A comma-seperated string containing the client's chosen /// options. public string BuildOptionsString() { List list = new List(); foreach (string key in Options) { if (String.IsNullOrEmpty(Options[key]) || "true".Equals( Options[key], StringComparison.InvariantCultureIgnoreCase)) list.Add(key); else list.Add(key + "=" + Options[key]); } return String.Join(",", list.ToArray()); } } }