using System; using System.Collections.Specialized; using System.IO; namespace S22.Sasl.Mechanisms.Srp { /// <summary> /// Represents the first message sent by the server in response to an /// initial client-response. /// </summary> internal class ServerMessage1 { /// <summary> /// The safe prime modulus sent by the server. /// </summary> public Mpi SafePrimeModulus { get; set; } /// <summary> /// The generator sent by the server. /// </summary> public Mpi Generator { get; set; } /// <summary> /// The user's password salt. /// </summary> public byte[] Salt { get; set; } /// <summary> /// The server's ephemeral public key. /// </summary> public Mpi PublicKey { get; set; } /// <summary> /// The options list indicating available security services. /// </summary> public NameValueCollection Options { get; set; } /// <summary> /// The raw options as received from the server. /// </summary> public string RawOptions { get; set; } /// <summary> /// Deserializes a new instance of the ServerMessage1 class from the /// specified buffer of bytes. /// </summary> /// <param name="buffer">The byte buffer to deserialize the ServerMessage1 /// instance from.</param> /// <returns>An instance of the ServerMessage1 class deserialized from the /// specified byte array.</returns> /// <exception cref="FormatException">Thrown if the byte buffer does not /// contain valid data.</exception> public static ServerMessage1 Deserialize(byte[] buffer) { using (var ms = new MemoryStream(buffer)) { using (var r = new BinaryReader(ms)) { uint bufferLength = r.ReadUInt32(true); // We don't support re-using previous sessions. byte reuse = r.ReadByte(); if (reuse != 0) { throw new FormatException("Unexpected re-use parameter value: " + reuse); } Mpi N = r.ReadMpi(); Mpi g = r.ReadMpi(); OctetSequence salt = r.ReadOs(); Mpi B = r.ReadMpi(); Utf8String L = r.ReadUtf8String(); return new ServerMessage1() { Generator = g, PublicKey = B, Salt = salt.Value, SafePrimeModulus = N, Options = ParseOptions(L.Value), RawOptions = L.Value }; } } } /// <summary> /// Parses the options string sent by the server. /// </summary> /// <param name="s">A comma-delimited options string.</param> /// <returns>An initialized instance of the NameValueCollection class /// containing the parsed server options.</returns> public static NameValueCollection ParseOptions(string s) { NameValueCollection coll = new NameValueCollection(); string[] parts = s.Split(','); foreach (string p in parts) { int index = p.IndexOf('='); if (index < 0) { coll.Add(p, "true"); } else { string name = p.Substring(0, index), value = p.Substring(index + 1); coll.Add(name, value); } } return coll; } } }