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