Commit 82f88814 authored by Honfika's avatar Honfika

Parse ssl extensions

parent c8338bfe
......@@ -17,10 +17,11 @@ namespace Titanium.Web.Proxy.Helpers
position = startPosition;
}
public int Available => baseStream.Available - position;
public async Task<bool> EnsureBufferLength(int length)
{
var val = await baseStream.PeekByteAsync(length - 1);
var val = await baseStream.PeekByteAsync(position + length - 1);
return val != -1;
}
......
......@@ -36,6 +36,12 @@ namespace Titanium.Web.Proxy.Http
public byte[] SessionId { get; set; }
public byte[] CiphersData { get; set; }
public byte[] CompressionData { get; set; }
public List<Extension> Extensions { get; set; }
private static string SslVersionToString(int major, int minor)
{
string str = "Unknown";
......@@ -69,6 +75,15 @@ namespace Titanium.Web.Proxy.Http
sb.AppendLine($"\"Time\": {Time}");
sb.AppendLine($"SessionID: {string.Join(" ", SessionId.Select(x => x.ToString("X2")))}");
if (Extensions != null)
{
sb.AppendLine("Extensions:");
foreach (var extension in Extensions)
{
sb.AppendLine($"{extension.Name}: {extension.Data}");
}
}
return sb.ToString();
}
}
......
......@@ -45,8 +45,8 @@ namespace Titanium.Web.Proxy.Http
var peekStream = new CustomBufferedPeekStream(clientStream, 1);
//should contain at least 43 bytes
int requiredLength = 43; // 2 version + 2 length + 1 type + 3 length(?) + 2 version + 32 random + 1 sessionid length
if (!await peekStream.EnsureBufferLength(requiredLength))
// 2 version + 2 length + 1 type + 3 length(?) + 2 version + 32 random + 1 sessionid length
if (!await peekStream.EnsureBufferLength(43))
{
return false;
}
......@@ -56,7 +56,7 @@ namespace Titanium.Web.Proxy.Http
int minorVersion = peekStream.ReadByte();
int length = peekStream.ReadInt16();
if (peekStream.ReadByte() != 0x01)
{
// should be ClientHello
......@@ -71,8 +71,8 @@ namespace Titanium.Web.Proxy.Http
byte[] random = peekStream.ReadBytes(32);
length = peekStream.ReadByte();
requiredLength += length + 2; // sessionid + 2 data length
if (!await peekStream.EnsureBufferLength(requiredLength))
// sessionid + 2 ciphersData length
if (!await peekStream.EnsureBufferLength(length + 2))
{
return false;
}
......@@ -81,13 +81,13 @@ namespace Titanium.Web.Proxy.Http
length = peekStream.ReadInt16();
requiredLength += length + 1; // data + data2 length
if (!await peekStream.EnsureBufferLength(requiredLength))
// ciphersData + compressionData length
if (!await peekStream.EnsureBufferLength(length + 1))
{
return false;
}
byte[] data = peekStream.ReadBytes(length);
byte[] ciphersData = peekStream.ReadBytes(length);
length = peekStream.ReadByte();
if (length < 1)
......@@ -95,26 +95,31 @@ namespace Titanium.Web.Proxy.Http
return false;
}
requiredLength += length; // data2
if (!await peekStream.EnsureBufferLength(requiredLength))
// compressionData
if (!await peekStream.EnsureBufferLength(length))
{
return false;
}
byte[] data2 = peekStream.ReadBytes(length);
byte[] compressionData = peekStream.ReadBytes(length);
byte[] data3 = null;
List<Extension> extensions = null;
if (majorVersion > 3 || majorVersion == 3 && minorVersion >= 1)
{
requiredLength += 2;
if (await peekStream.EnsureBufferLength(requiredLength))
if (await peekStream.EnsureBufferLength(2))
{
length = peekStream.ReadInt16();
requiredLength += length;
if (await peekStream.EnsureBufferLength(requiredLength))
if (await peekStream.EnsureBufferLength(length))
{
data3 = peekStream.ReadBytes(length);
extensions = new List<Extension>();
while (peekStream.Available > 3)
{
int id = peekStream.ReadInt16();
length = peekStream.ReadInt16();
byte[] data = peekStream.ReadBytes(length);
extensions.Add(GetExtension(id, data));
}
}
}
}
......@@ -125,6 +130,9 @@ namespace Titanium.Web.Proxy.Http
MinorVersion = minorVersion,
Random = random,
SessionId = sessionId,
CiphersData = ciphersData,
CompressionData = compressionData,
Extensions = extensions,
};
return true;
......@@ -132,5 +140,408 @@ namespace Titanium.Web.Proxy.Http
return false;
}
private static Extension GetExtension(int value, byte[] data)
{
string name = GetExtensionName(value);
string dataStr = GetExtensionData(value, data);
return new Extension(value, name, dataStr);
}
private static string GetExtensionData(int value, byte[] data)
{
//https://www.iana.org/assignments/tls-extensiontype-values/tls-extensiontype-values.xhtml
switch (value)
{
case 0:
var stringBuilder = new StringBuilder();
int index = 2;
while (index < data.Length)
{
int nameType = data[index];
int count = (data[index + 1] << 8) + data[index + 2];
string str = Encoding.ASCII.GetString(data, index + 3, count);
if (nameType == 0)
{
stringBuilder.AppendFormat("{0}{1}", stringBuilder.Length > 1 ? "; " : string.Empty, str);
}
index += 3 + count;
}
return stringBuilder.ToString();
case 5:
if (data.Length == 5 && data[0] == 1 && data[1] == 0 && data[2] == 0 && data[3] == 0 && data[4] == 0)
{
return "OCSP - Implicit Responder";
}
return ByteArrayToString(data);
case 10:
return GetSupportedGroup(data);
case 11:
return GetEcPointFormats(data);
case 13:
return GetSignatureAlgorithms(data);
case 16:
return GetApplicationLayerProtocolNegotiation(data);
case 35655:
return $"{data.Length} bytes";
default:
return ByteArrayToString(data);
}
}
private static string GetSupportedGroup(byte[] data)
{
//https://datatracker.ietf.org/doc/draft-ietf-tls-rfc4492bis/?include_text=1
List<string> list = new List<string>();
if (data.Length < 2)
{
return string.Empty;
}
int i = 2;
while (i < data.Length - 1)
{
int namedCurve = (data[i] << 8) + data[i + 1];
switch (namedCurve)
{
case 1:
list.Add("sect163k1 [0x1]"); //deprecated
break;
case 2:
list.Add("sect163r1 [0x2]"); //deprecated
break;
case 3:
list.Add("sect163r2 [0x3]"); //deprecated
break;
case 4:
list.Add("sect193r1 [0x4]"); //deprecated
break;
case 5:
list.Add("sect193r2 [0x5]"); //deprecated
break;
case 6:
list.Add("sect233k1 [0x6]"); //deprecated
break;
case 7:
list.Add("sect233r1 [0x7]"); //deprecated
break;
case 8:
list.Add("sect239k1 [0x8]"); //deprecated
break;
case 9:
list.Add("sect283k1 [0x9]"); //deprecated
break;
case 10:
list.Add("sect283r1 [0xA]"); //deprecated
break;
case 11:
list.Add("sect409k1 [0xB]"); //deprecated
break;
case 12:
list.Add("sect409r1 [0xC]"); //deprecated
break;
case 13:
list.Add("sect571k1 [0xD]"); //deprecated
break;
case 14:
list.Add("sect571r1 [0xE]"); //deprecated
break;
case 15:
list.Add("secp160k1 [0xF]"); //deprecated
break;
case 16:
list.Add("secp160r1 [0x10]"); //deprecated
break;
case 17:
list.Add("secp160r2 [0x11]"); //deprecated
break;
case 18:
list.Add("secp192k1 [0x12]"); //deprecated
break;
case 19:
list.Add("secp192r1 [0x13]"); //deprecated
break;
case 20:
list.Add("secp224k1 [0x14]"); //deprecated
break;
case 21:
list.Add("secp224r1 [0x15]"); //deprecated
break;
case 22:
list.Add("secp256k1 [0x16]"); //deprecated
break;
case 23:
list.Add("secp256r1 [0x17]");
break;
case 24:
list.Add("secp384r1 [0x18]");
break;
case 25:
list.Add("secp521r1 [0x19]");
break;
case 26:
list.Add("brainpoolP256r1 [0x1A]");
break;
case 27:
list.Add("brainpoolP384r1 [0x1B]");
break;
case 28:
list.Add("brainpoolP512r1 [0x1C]");
break;
case 29:
list.Add("x25519 [0x1D]");
break;
case 30:
list.Add("x448 [0x1E]");
break;
case 256:
list.Add("ffdhe2048 [0x0100]");
break;
case 257:
list.Add("ffdhe3072 [0x0101]");
break;
case 258:
list.Add("ffdhe4096 [0x0102]");
break;
case 259:
list.Add("ffdhe6144 [0x0103]");
break;
case 260:
list.Add("ffdhe8192 [0x0104]");
break;
case 65281:
list.Add("arbitrary_explicit_prime_curves [0xFF01]"); //deprecated
break;
case 65282:
list.Add("arbitrary_explicit_char2_curves [0xFF02]"); //deprecated
break;
default:
list.Add($"unknown [0x{namedCurve:X4}]");
break;
}
i += 2;
}
return string.Join(", ", list.ToArray());
}
private static string GetEcPointFormats(byte[] data)
{
List<string> list = new List<string>();
if (data.Length < 1)
{
return string.Empty;
}
int i = 1;
while (i < data.Length)
{
switch (data[i])
{
case 0:
list.Add("uncompressed [0x0]");
break;
case 1:
list.Add("ansiX962_compressed_prime [0x1]");
break;
case 2:
list.Add("ansiX962_compressed_char2 [0x2]");
break;
default:
list.Add($"unknown [0x{data[i]:X2}]");
break;
}
i += 2;
}
return string.Join(", ", list.ToArray());
}
private static string GetSignatureAlgorithms(byte[] data)
{
// https://www.iana.org/assignments/tls-parameters/tls-parameters.xhtml
int num = (data[0] << 8) + data[1];
var sb = new StringBuilder();
int index = 2;
while (index < num + 2)
{
switch (data[index])
{
case 0:
sb.Append("none");
break;
case 1:
sb.Append("md5");
break;
case 2:
sb.Append("sha1");
break;
case 3:
sb.Append("sha224");
break;
case 4:
sb.Append("sha256");
break;
case 5:
sb.Append("sha384");
break;
case 6:
sb.Append("sha512");
break;
case 8:
sb.Append("Intrinsic");
break;
default:
sb.AppendFormat("Unknown[0x{0:X2}]", data[index]);
break;
}
sb.AppendFormat("_");
switch (data[index + 1])
{
case 0:
sb.Append("anonymous");
break;
case 1:
sb.Append("rsa");
break;
case 2:
sb.Append("dsa");
break;
case 3:
sb.Append("ecdsa");
break;
case 7:
sb.Append("ed25519");
break;
case 8:
sb.Append("ed448");
break;
default:
sb.AppendFormat("Unknown[0x{0:X2}]", data[index + 1]);
break;
}
sb.AppendFormat(", ");
index += 2;
}
if (sb.Length > 1)
sb.Length -= 2;
return sb.ToString();
}
private static string GetApplicationLayerProtocolNegotiation(byte[] data)
{
List<string> stringList = new List<string>();
int index = 2;
while (index < data.Length)
{
int count = data[index];
stringList.Add(Encoding.ASCII.GetString(data, index + 1, count));
index += 1 + count;
}
return string.Join(", ", stringList.ToArray());
}
private static string GetExtensionName(int value)
{
//https://www.iana.org/assignments/tls-extensiontype-values/tls-extensiontype-values.xhtml
switch (value)
{
case 0:
return "server_name";
case 1:
return "max_fragment_length";
case 2:
return "client_certificate_url";
case 3:
return "trusted_ca_keys";
case 4:
return "truncated_hmac";
case 5:
return "status_request";
case 6:
return "user_mapping";
case 7:
return "client_authz";
case 8:
return "server_authz";
case 9:
return "cert_type";
case 10:
return "supported_groups"; // renamed from "elliptic_curves"
case 11:
return "ec_point_formats";
case 12:
return "srp";
case 13:
return "signature_algorithms";
case 14:
return "use_srtp";
case 15:
return "heartbeat";
case 16:
return "ALPN"; // application_layer_protocol_negotiation
case 17:
return "status_request_v2";
case 18:
return "signed_certificate_timestamp";
case 19:
return "client_certificate_type";
case 20:
return "server_certificate_type";
case 21:
return "padding";
case 22:
return "encrypt_then_mac";
case 23:
return "extended_master_secret";
case 24:
return "token_binding"; // TEMPORARY - registered 2016-02-04, extension registered 2017-01-12, expires 2018-02-04
case 25:
return "cached_info";
case 35:
return "SessionTicket TLS";
case 13172:
return "next_protocol_negotiation";
case 30031:
case 30032:
return "channel_id"; // Google
case 35655:
return "draft-agl-tls-padding";
case 65281:
return "renegotiation_info";
default:
return "unknown";
}
}
private static string ByteArrayToString(byte[] data)
{
return string.Join(" ", data.Select(x => x.ToString("X2")));
}
}
public class Extension
{
public int Value { get; set; }
public string Name { get; set; }
public string Data { get; set; }
public Extension(int value, string name, string data)
{
Value = value;
Name = name;
Data = data;
}
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment