From c16004e3afb8aa2524aeec88aa7c9c67400e93c1 Mon Sep 17 00:00:00 2001 From: Thomas Pornin Date: Tue, 3 Jul 2018 00:02:34 +0200 Subject: [PATCH] Added command-line client (for debug only). --- CLI/Client.cs | 355 +++++++++++++++++++++++++++++++++++ {Twrch => IO}/MergeStream.cs | 20 +- SSLTLS/SSL.cs | 300 +++++++++++++++++++++++++++++ Twrch/Twrch.cs | 21 ++- build.cmd | 14 +- build.sh | 6 +- 6 files changed, 699 insertions(+), 17 deletions(-) create mode 100644 CLI/Client.cs rename {Twrch => IO}/MergeStream.cs (88%) diff --git a/CLI/Client.cs b/CLI/Client.cs new file mode 100644 index 0000000..25778cd --- /dev/null +++ b/CLI/Client.cs @@ -0,0 +1,355 @@ +/* + * Copyright (c) 2018 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Net.Sockets; +using System.Text; +using System.Threading; + +using Asn1; +using Crypto; +using IO; +using SSLTLS; +using XKeys; + +/* + * A simple command-line application that runs a client that connects + * to a provided server. This is meant for debug purposes. + */ + +public class Client { + + public static void Main(string[] args) + { + try { + new Client().Run(args); + } catch (Exception e) { + Console.WriteLine(e.ToString()); + Environment.Exit(1); + } + } + + void Run(string[] args) + { + bool verbose = true; + bool trace = false; + string host = null; + string sni = null; + List csNames = null; + List hsNames = null; + int vmin = 0; + int vmax = 0; + for (int i = 0; i < args.Length; i ++) { + string a = args[i]; + if (!a.StartsWith("-")) { + if (host != null) { + throw new Exception( + "duplicate host name"); + } + host = a; + continue; + } + a = a.Substring(1).ToLowerInvariant(); + switch (a) { + case "v": + verbose = true; + break; + case "q": + verbose = false; + break; + case "trace": + trace = true; + break; + case "sni": + if (sni != null) { + throw new Exception( + "duplicate SNI"); + } + if (++ i >= args.Length) { + throw new Exception( + "no SNI provided"); + } + sni = args[i]; + break; + case "nosni": + if (sni != null) { + throw new Exception( + "duplicate SNI"); + } + sni = ""; + break; + case "cs": + if (++ i >= args.Length) { + throw new Exception( + "no cipher names provided"); + } + if (csNames == null) { + csNames = new List(); + } + AddNames(csNames, args[i]); + break; + case "hs": + if (++ i >= args.Length) { + throw new Exception( + "no hash-and-sign provided"); + } + if (hsNames == null) { + hsNames = new List(); + } + AddNames(hsNames, args[i]); + break; + case "vmin": + if (vmin != 0) { + throw new Exception( + "duplicate minimum version"); + } + if (++ i >= args.Length) { + throw new Exception( + "no minimum version provided"); + } + vmin = SSL.GetVersionByName(args[i]); + break; + case "vmax": + if (vmax != 0) { + throw new Exception( + "duplicate maximum version"); + } + if (++ i >= args.Length) { + throw new Exception( + "no maximum version provided"); + } + vmax = SSL.GetVersionByName(args[i]); + break; + default: + throw new Exception(string.Format( + "Unknown option: '-{0}'", a)); + } + } + + if (host == null) { + throw new Exception("no host name provided"); + } + int j = host.LastIndexOf(':'); + int port; + if (j < 0) { + port = 443; + } else { + if (!Int32.TryParse(host.Substring(j + 1), out port) + || port <= 0 || port > 65535) + { + throw new Exception("invalid port number"); + } + host = host.Substring(0, j); + } + if (sni == null) { + sni = host; + } + int[] css = null; + if (csNames != null) { + css = new int[csNames.Count]; + for (int i = 0; i < css.Length; i ++) { + css[i] = SSL.GetSuiteByName(csNames[i]); + } + } + int[] hss = null; + if (hsNames != null) { + hss = new int[hsNames.Count]; + for (int i = 0; i < hss.Length; i ++) { + hss[i] = SSL.GetHashAndSignByName(hsNames[i]); + } + } + if (vmin != 0 && vmax != 0 && vmin > vmax) { + throw new Exception("invalid version range"); + } + + /* + * Connect to the designated server. + */ + TcpClient tc = new TcpClient(host, port); + Socket sock = tc.Client; + Stream ns = tc.GetStream(); + if (trace) { + MergeStream ms = new MergeStream(ns, ns); + ms.Debug = Console.Out; + ns = ms; + } + SSLClient ssl = new SSLClient(ns); + if (sni != "") { + ssl.ServerName = sni; + } + if (css != null) { + ssl.SupportedCipherSuites = css; + } + if (hss != null) { + ssl.SupportedHashAndSign = hss; + } + if (vmin != 0) { + ssl.VersionMin = vmin; + } + if (vmax != 0) { + ssl.VersionMax = vmax; + } + + /* + * This is a debug tool; we accept the server certificate + * without validation. + */ + ssl.ServerCertValidator = SSLClient.InsecureCertValidator; + + /* + * Force a Flush. There is no application data to flush + * at this point, but as a side-effect it forces the + * handshake to complete. + */ + ssl.Flush(); + + if (verbose) { + Console.WriteLine("Handshake completed:"); + Console.WriteLine(" Version = {0}", + SSL.VersionName(ssl.Version)); + Console.WriteLine(" Cipher suite = {0}", + SSL.CipherSuiteName(ssl.CipherSuite)); + } + + /* + * Now relay data back and forth between the connection + * and the console. Since the underlying SSL stream does + * not support simultaneous reads and writes, we use + * the following approximation: + * + * - We poll on the socket for incoming data. When there + * is some activity, we assume that some application + * data (or closure) follows, and we read it. It is + * then immediately written out (synchronously) on + * standard output. + * + * - When waiting for read activity on the socket, we + * regularly (every 200 ms) check for data to read on + * standard input. If there is, we read it, and send + * it synchronously on the SSL stream. + * + * - The data reading from console is performed by + * another thread. + * + * Since SSL records are read one by one, we know that, + * by using a buffer larger than 16 kB, a single Read() + * call cannot leave any buffered application data. + */ + ssl.CloseSub = false; + Thread t = new Thread(new ThreadStart(CRThread)); + t.IsBackground = true; + t.Start(); + byte[] buf = new byte[16384]; + Stream stdout = Console.OpenStandardOutput(); + for (;;) { + if (sock.Poll(200000, SelectMode.SelectRead)) { + int rlen = ssl.Read(buf, 0, buf.Length); + if (rlen < 0) { + Console.WriteLine( + "Connection closed.\n"); + break; + } + stdout.Write(buf, 0, rlen); + } else { + while (CRHasData()) { + int rlen = CRRead(buf, 0, buf.Length); + if (rlen < 0) { + ssl.Close(); + break; + } + if (rlen > 0) { + ssl.Write(buf, 0, rlen); + } + } + } + } + sock.Close(); + } + + static void AddNames(List d, string str) + { + foreach (string name in str.Split( + new char[] { ',', ':', ';' }, + StringSplitOptions.RemoveEmptyEntries)) + { + d.Add(name.Trim()); + } + } + + object consoleReadLock = new object(); + byte[] crBuf = new byte[16384]; + int crPtr = 0; + bool crClosed = false; + + bool CRHasData() + { + lock (consoleReadLock) { + return crPtr != 0 || crClosed; + } + } + + int CRRead(byte[] buf, int off, int len) + { + lock (consoleReadLock) { + if (crPtr == 0 && crClosed) { + return -1; + } + int rlen = Math.Min(len, crPtr); + Array.Copy(crBuf, 0, buf, off, rlen); + if (rlen > 0 && rlen < crPtr) { + Array.Copy(crBuf, rlen, crBuf, 0, crPtr - rlen); + } + crPtr -= rlen; + Monitor.PulseAll(consoleReadLock); + return rlen; + } + } + + void CRThread() + { + byte[] buf = new byte[crBuf.Length]; + Stream stdin = Console.OpenStandardInput(); + + for (;;) { + lock (consoleReadLock) { + while (crPtr == crBuf.Length) { + Monitor.Wait(consoleReadLock); + } + } + int rlen = stdin.Read(buf, 0, buf.Length); + lock (consoleReadLock) { + Monitor.PulseAll(consoleReadLock); + if (rlen < 0) { + crClosed = true; + break; + } + Array.Copy(buf, 0, crBuf, crPtr, rlen); + crPtr += rlen; + } + } + } +} diff --git a/Twrch/MergeStream.cs b/IO/MergeStream.cs similarity index 88% rename from Twrch/MergeStream.cs rename to IO/MergeStream.cs index 4a5ac29..d233af2 100644 --- a/Twrch/MergeStream.cs +++ b/IO/MergeStream.cs @@ -25,6 +25,8 @@ using System; using System.IO; +namespace IO { + /* * This class merges two underlying streams (one for reading, the other * for writing) into a single Stream object. It can also optionally dump @@ -32,15 +34,25 @@ using System.IO; * stream (for debugging purposes). */ -internal class MergeStream : Stream { +public class MergeStream : Stream { Stream subIn, subOut; - internal TextWriter Debug { + /* + * Text stream on which to write an hexadecimal dump of the data + * which is read from or written to this stream. If null (the + * default), then no dump is written. + */ + public TextWriter Debug { get; set; } - internal MergeStream(Stream subIn, Stream subOut) + /* + * Create this stream over the two underlying substreams: + * 'subIn', from which data is read, and 'subOut', to which data + * is written. The two substreams may be the same object. + */ + public MergeStream(Stream subIn, Stream subOut) { this.subIn = subIn; this.subOut = subOut; @@ -182,3 +194,5 @@ internal class MergeStream : Stream { } } } + +} diff --git a/SSLTLS/SSL.cs b/SSLTLS/SSL.cs index 5250e5d..4059194 100644 --- a/SSLTLS/SSL.cs +++ b/SSLTLS/SSL.cs @@ -288,6 +288,36 @@ public sealed class SSL { return String.Format("UNKNOWN:0x{0:X4}", version); } + /* + * Parse a version name. + */ + public static int GetVersionByName(string s) + { + string t = s.Trim().Replace(" ", "").Replace(".", "") + .Replace("-", "").ToUpperInvariant(); + switch (t) { + case "SSL3": + case "SSLV3": + case "SSL30": + case "SSLV30": + return SSL30; + case "TLS1": + case "TLSV1": + case "TLS10": + case "TLSV10": + return TLS10; + case "TLS11": + case "TLSV11": + return TLS11; + case "TLS12": + case "TLSV12": + return TLS12; + default: + throw new Exception(string.Format( + "Unknown protocol version: '{0}'", s)); + } + } + /* * Get a human-readable name for a cipher suite. */ @@ -497,6 +527,218 @@ public sealed class SSL { } } + /* + * Parse a cipher suite name. + */ + public static int GetSuiteByName(string s) + { + string t = s.Trim().Replace("_", "").Replace("-", "") + .ToUpperInvariant(); + if (t.StartsWith("TLS") || t.StartsWith("SSL")) { + t = t.Substring(3); + } + switch (t) { + case "NULLWITHNULLNULL": + return NULL_WITH_NULL_NULL; + case "RSAWITHNULLMD5": + return RSA_WITH_NULL_MD5; + case "RSAWITHNULLSHA": + return RSA_WITH_NULL_SHA; + case "RSAWITHNULLSHA256": + return RSA_WITH_NULL_SHA256; + case "RSAWITHRC4128MD5": + return RSA_WITH_RC4_128_MD5; + case "RSAWITHRC4128SHA": + return RSA_WITH_RC4_128_SHA; + case "RSAWITH3DESEDECBCSHA": + return RSA_WITH_3DES_EDE_CBC_SHA; + case "RSAWITHAES128CBCSHA": + return RSA_WITH_AES_128_CBC_SHA; + case "RSAWITHAES256CBCSHA": + return RSA_WITH_AES_256_CBC_SHA; + case "RSAWITHAES128CBCSHA256": + return RSA_WITH_AES_128_CBC_SHA256; + case "RSAWITHAES256CBCSHA256": + return RSA_WITH_AES_256_CBC_SHA256; + case "DHDSSWITH3DESEDECBCSHA": + return DH_DSS_WITH_3DES_EDE_CBC_SHA; + case "DHRSAWITH3DESEDECBCSHA": + return DH_RSA_WITH_3DES_EDE_CBC_SHA; + case "DHEDSSWITH3DESEDECBCSHA": + return DHE_DSS_WITH_3DES_EDE_CBC_SHA; + case "DHERSAWITH3DESEDECBCSHA": + return DHE_RSA_WITH_3DES_EDE_CBC_SHA; + case "DHDSSWITHAES128CBCSHA": + return DH_DSS_WITH_AES_128_CBC_SHA; + case "DHRSAWITHAES128CBCSHA": + return DH_RSA_WITH_AES_128_CBC_SHA; + case "DHEDSSWITHAES128CBCSHA": + return DHE_DSS_WITH_AES_128_CBC_SHA; + case "DHERSAWITHAES128CBCSHA": + return DHE_RSA_WITH_AES_128_CBC_SHA; + case "DHDSSWITHAES256CBCSHA": + return DH_DSS_WITH_AES_256_CBC_SHA; + case "DHRSAWITHAES256CBCSHA": + return DH_RSA_WITH_AES_256_CBC_SHA; + case "DHEDSSWITHAES256CBCSHA": + return DHE_DSS_WITH_AES_256_CBC_SHA; + case "DHERSAWITHAES256CBCSHA": + return DHE_RSA_WITH_AES_256_CBC_SHA; + case "DHDSSWITHAES128CBCSHA256": + return DH_DSS_WITH_AES_128_CBC_SHA256; + case "DHRSAWITHAES128CBCSHA256": + return DH_RSA_WITH_AES_128_CBC_SHA256; + case "DHEDSSWITHAES128CBCSHA256": + return DHE_DSS_WITH_AES_128_CBC_SHA256; + case "DHERSAWITHAES128CBCSHA256": + return DHE_RSA_WITH_AES_128_CBC_SHA256; + case "DHDSSWITHAES256CBCSHA256": + return DH_DSS_WITH_AES_256_CBC_SHA256; + case "DHRSAWITHAES256CBCSHA256": + return DH_RSA_WITH_AES_256_CBC_SHA256; + case "DHEDSSWITHAES256CBCSHA256": + return DHE_DSS_WITH_AES_256_CBC_SHA256; + case "DHERSAWITHAES256CBCSHA256": + return DHE_RSA_WITH_AES_256_CBC_SHA256; + case "DHANONWITHRC4128MD5": + return DH_anon_WITH_RC4_128_MD5; + case "DHANONWITH3DESEDECBCSHA": + return DH_anon_WITH_3DES_EDE_CBC_SHA; + case "DHANONWITHAES128CBCSHA": + return DH_anon_WITH_AES_128_CBC_SHA; + case "DHANONWITHAES256CBCSHA": + return DH_anon_WITH_AES_256_CBC_SHA; + case "DHANONWITHAES128CBCSHA256": + return DH_anon_WITH_AES_128_CBC_SHA256; + case "DHANONWITHAES256CBCSHA256": + return DH_anon_WITH_AES_256_CBC_SHA256; + case "ECDHECDSAWITHNULLSHA": + return ECDH_ECDSA_WITH_NULL_SHA; + case "ECDHECDSAWITHRC4128SHA": + return ECDH_ECDSA_WITH_RC4_128_SHA; + case "ECDHECDSAWITH3DESEDECBCSHA": + return ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA; + case "ECDHECDSAWITHAES128CBCSHA": + return ECDH_ECDSA_WITH_AES_128_CBC_SHA; + case "ECDHECDSAWITHAES256CBCSHA": + return ECDH_ECDSA_WITH_AES_256_CBC_SHA; + case "ECDHEECDSAWITHNULLSHA": + return ECDHE_ECDSA_WITH_NULL_SHA; + case "ECDHEECDSAWITHRC4128SHA": + return ECDHE_ECDSA_WITH_RC4_128_SHA; + case "ECDHEECDSAWITH3DESEDECBCSHA": + return ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA; + case "ECDHEECDSAWITHAES128CBCSHA": + return ECDHE_ECDSA_WITH_AES_128_CBC_SHA; + case "ECDHEECDSAWITHAES256CBCSHA": + return ECDHE_ECDSA_WITH_AES_256_CBC_SHA; + case "ECDHRSAWITHNULLSHA": + return ECDH_RSA_WITH_NULL_SHA; + case "ECDHRSAWITHRC4128SHA": + return ECDH_RSA_WITH_RC4_128_SHA; + case "ECDHRSAWITH3DESEDECBCSHA": + return ECDH_RSA_WITH_3DES_EDE_CBC_SHA; + case "ECDHRSAWITHAES128CBCSHA": + return ECDH_RSA_WITH_AES_128_CBC_SHA; + case "ECDHRSAWITHAES256CBCSHA": + return ECDH_RSA_WITH_AES_256_CBC_SHA; + case "ECDHERSAWITHNULLSHA": + return ECDHE_RSA_WITH_NULL_SHA; + case "ECDHERSAWITHRC4128SHA": + return ECDHE_RSA_WITH_RC4_128_SHA; + case "ECDHERSAWITH3DESEDECBCSHA": + return ECDHE_RSA_WITH_3DES_EDE_CBC_SHA; + case "ECDHERSAWITHAES128CBCSHA": + return ECDHE_RSA_WITH_AES_128_CBC_SHA; + case "ECDHERSAWITHAES256CBCSHA": + return ECDHE_RSA_WITH_AES_256_CBC_SHA; + case "ECDHANONWITHNULLSHA": + return ECDH_anon_WITH_NULL_SHA; + case "ECDHANONWITHRC4128SHA": + return ECDH_anon_WITH_RC4_128_SHA; + case "ECDHANONWITH3DESEDECBCSHA": + return ECDH_anon_WITH_3DES_EDE_CBC_SHA; + case "ECDHANONWITHAES128CBCSHA": + return ECDH_anon_WITH_AES_128_CBC_SHA; + case "ECDHANONWITHAES256CBCSHA": + return ECDH_anon_WITH_AES_256_CBC_SHA; + case "RSAWITHAES128GCMSHA256": + return RSA_WITH_AES_128_GCM_SHA256; + case "RSAWITHAES256GCMSHA384": + return RSA_WITH_AES_256_GCM_SHA384; + case "DHERSAWITHAES128GCMSHA256": + return DHE_RSA_WITH_AES_128_GCM_SHA256; + case "DHERSAWITHAES256GCMSHA384": + return DHE_RSA_WITH_AES_256_GCM_SHA384; + case "DHRSAWITHAES128GCMSHA256": + return DH_RSA_WITH_AES_128_GCM_SHA256; + case "DHRSAWITHAES256GCMSHA384": + return DH_RSA_WITH_AES_256_GCM_SHA384; + case "DHEDSSWITHAES128GCMSHA256": + return DHE_DSS_WITH_AES_128_GCM_SHA256; + case "DHEDSSWITHAES256GCMSHA384": + return DHE_DSS_WITH_AES_256_GCM_SHA384; + case "DHDSSWITHAES128GCMSHA256": + return DH_DSS_WITH_AES_128_GCM_SHA256; + case "DHDSSWITHAES256GCMSHA384": + return DH_DSS_WITH_AES_256_GCM_SHA384; + case "DHANONWITHAES128GCMSHA256": + return DH_anon_WITH_AES_128_GCM_SHA256; + case "DHANONWITHAES256GCMSHA384": + return DH_anon_WITH_AES_256_GCM_SHA384; + case "ECDHEECDSAWITHAES128CBCSHA256": + return ECDHE_ECDSA_WITH_AES_128_CBC_SHA256; + case "ECDHEECDSAWITHAES256CBCSHA384": + return ECDHE_ECDSA_WITH_AES_256_CBC_SHA384; + case "ECDHECDSAWITHAES128CBCSHA256": + return ECDH_ECDSA_WITH_AES_128_CBC_SHA256; + case "ECDHECDSAWITHAES256CBCSHA384": + return ECDH_ECDSA_WITH_AES_256_CBC_SHA384; + case "ECDHERSAWITHAES128CBCSHA256": + return ECDHE_RSA_WITH_AES_128_CBC_SHA256; + case "ECDHERSAWITHAES256CBCSHA384": + return ECDHE_RSA_WITH_AES_256_CBC_SHA384; + case "ECDHRSAWITHAES128CBCSHA256": + return ECDH_RSA_WITH_AES_128_CBC_SHA256; + case "ECDHRSAWITHAES256CBCSHA384": + return ECDH_RSA_WITH_AES_256_CBC_SHA384; + case "ECDHEECDSAWITHAES128GCMSHA256": + return ECDHE_ECDSA_WITH_AES_128_GCM_SHA256; + case "ECDHEECDSAWITHAES256GCMSHA384": + return ECDHE_ECDSA_WITH_AES_256_GCM_SHA384; + case "ECDHECDSAWITHAES128GCMSHA256": + return ECDH_ECDSA_WITH_AES_128_GCM_SHA256; + case "ECDHECDSAWITHAES256GCMSHA384": + return ECDH_ECDSA_WITH_AES_256_GCM_SHA384; + case "ECDHERSAWITHAES128GCMSHA256": + return ECDHE_RSA_WITH_AES_128_GCM_SHA256; + case "ECDHERSAWITHAES256GCMSHA384": + return ECDHE_RSA_WITH_AES_256_GCM_SHA384; + case "ECDHRSAWITHAES128GCMSHA256": + return ECDH_RSA_WITH_AES_128_GCM_SHA256; + case "ECDHRSAWITHAES256GCMSHA384": + return ECDH_RSA_WITH_AES_256_GCM_SHA384; + case "ECDHERSAWITHCHACHA20POLY1305SHA256": + return ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256; + case "ECDHEECDSAWITHCHACHA20POLY1305SHA256": + return ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256; + case "DHERSAWITHCHACHA20POLY1305SHA256": + return DHE_RSA_WITH_CHACHA20_POLY1305_SHA256; + case "PSKWITHCHACHA20POLY1305SHA256": + return PSK_WITH_CHACHA20_POLY1305_SHA256; + case "ECDHEPSKWITHCHACHA20POLY1305SHA256": + return ECDHE_PSK_WITH_CHACHA20_POLY1305_SHA256; + case "DHEPSKWITHCHACHA20POLY1305SHA256": + return DHE_PSK_WITH_CHACHA20_POLY1305_SHA256; + case "RSAPSKWITHCHACHA20POLY1305SHA256": + return RSA_PSK_WITH_CHACHA20_POLY1305_SHA256; + + default: + throw new Exception(string.Format( + "Unknown cipher suite: '{0}'", s)); + } + } + /* * Get a human-readable name for a hash-and-sign algorithm. */ @@ -520,6 +762,33 @@ public sealed class SSL { } } + /* + * Parse a hash-and-sign name. + */ + public static int GetHashAndSignByName(string s) + { + string t = s.Trim().Replace(" ", "").Replace("_", "") + .Replace("-", "").Replace("/", "") + .ToUpperInvariant(); + switch (t) { + case "RSAMD5": return RSA_MD5; + case "RSASHA1": return RSA_SHA1; + case "RSASHA224": return RSA_SHA224; + case "RSASHA256": return RSA_SHA256; + case "RSASHA384": return RSA_SHA384; + case "RSASHA512": return RSA_SHA512; + case "ECDSAMD5": return ECDSA_MD5; + case "ECDSASHA1": return ECDSA_SHA1; + case "ECDSASHA224": return ECDSA_SHA224; + case "ECDSASHA256": return ECDSA_SHA256; + case "ECDSASHA384": return ECDSA_SHA384; + case "ECDSASHA512": return ECDSA_SHA512; + default: + throw new Exception(string.Format( + "Unknown hash-and-sign: '{0}'", s)); + } + } + /* * Get a human-readable name for a curve. */ @@ -535,6 +804,37 @@ public sealed class SSL { } } + /* + * Get a curve by name. + */ + public static int GetCurveByName(string s) + { + string t = s.Trim().Replace(" ", "").Replace("_", "") + .Replace("-", "").ToLowerInvariant(); + switch (t) { + case "c25519": + case "curve25519": + return Curve25519; + case "secp256r1": + case "p256": + case "nistp256": + case "prime256": + case "prime256v1": + return NIST_P256; + case "secp384r1": + case "p384": + case "nistp384": + return NIST_P384; + case "secp521r1": + case "p521": + case "nistp521": + return NIST_P521; + default: + throw new Exception(string.Format( + "Unknown curve: '{0}'", s)); + } + } + /* * Extract the public key from an encoded X.509 certificate. * This does NOT make any attempt at validating the certificate. diff --git a/Twrch/Twrch.cs b/Twrch/Twrch.cs index 6692d53..7ba0d7e 100644 --- a/Twrch/Twrch.cs +++ b/Twrch/Twrch.cs @@ -30,6 +30,7 @@ using System.Text; using Asn1; using Crypto; +using IO; using SSLTLS; using XKeys; @@ -248,11 +249,12 @@ public class Twrch { string[] r = JSON.GetStringArray(conf, "versions"); int[] vv = new int[r.Length]; for (int i = 0; i < r.Length; i ++) { - vv[i] = GetVersionByName(r[i]); + vv[i] = SSL.GetVersionByName(r[i]); } return vv; } + /* obsolete internal static int GetVersionByName(string s) { s = s.Replace(" ", "").Replace(".", "").ToUpperInvariant(); @@ -265,6 +267,7 @@ public class Twrch { "Unknown version: '{0}'", s)); } } + */ int[] GetCipherSuites() { @@ -276,11 +279,12 @@ public class Twrch { { int[] r = new int[ss.Length]; for (int i = 0; i < ss.Length; i ++) { - r[i] = GetSuiteByName(ss[i]); + r[i] = SSL.GetSuiteByName(ss[i]); } return r; } + /* obsolete internal static int GetSuiteByName(string s) { switch (s) { @@ -483,6 +487,7 @@ public class Twrch { "Unknown cipher suite: '{0}'", s)); } } + */ int[] GetHashAndSigns() { @@ -494,11 +499,12 @@ public class Twrch { { int[] r = new int[ss.Length]; for (int i = 0; i < ss.Length; i ++) { - r[i] = GetHashAndSignByName(ss[i]); + r[i] = SSL.GetHashAndSignByName(ss[i]); } return r; } + /* obsolete internal static int GetHashAndSignByName(string s) { switch (s) { @@ -519,6 +525,7 @@ public class Twrch { "Unknown hash-and-sign: '{0}'", s)); } } + */ int[] GetCurves() { @@ -529,11 +536,12 @@ public class Twrch { { int[] r = new int[ss.Length]; for (int i = 0; i < ss.Length; i ++) { - r[i] = GetCurveByName(ss[i]); + r[i] = SSL.GetCurveByName(ss[i]); } return r; } + /* obsolete internal static int GetCurveByName(string s) { switch (s) { @@ -546,6 +554,7 @@ public class Twrch { "Unknown curve: '{0}'", s)); } } + */ /* * RunEnum() builds and runs synthetic tests that exercise all @@ -982,7 +991,7 @@ public class Twrch { */ string svmin; if (JSON.TryGetString(obj, "versionMin", out svmin)) { - eng.VersionMin = GetVersionByName(svmin); + eng.VersionMin = SSL.GetVersionByName(svmin); } else { eng.VersionMin = versionMin; } @@ -992,7 +1001,7 @@ public class Twrch { */ string svmax; if (JSON.TryGetString(obj, "versionMax", out svmax)) { - eng.VersionMax = GetVersionByName(svmax); + eng.VersionMax = SSL.GetVersionByName(svmax); } else { eng.VersionMax = versionMax; } diff --git a/build.cmd b/build.cmd index 62d86fb..6885b43 100644 --- a/build.cmd +++ b/build.cmd @@ -1,5 +1,9 @@ -echo "Twrch..." -%SystemRoot%\Microsoft.NET\Framework\v4.0.30319\csc.exe /target:exe /out:Twrch.exe /main:Twrch Asn1\*.cs Crypto\*.cs SSLTLS\*.cs X500\*.cs XKeys\*.cs Twrch\*.cs - -echo "TestCrypto..." -%SystemRoot%\Microsoft.NET\Framework\v4.0.30319\csc.exe /target:exe /out:TestCrypto.exe /main:TestCrypto Tests\*.cs Asn1\*.cs Crypto\*.cs SSLTLS\*.cs X500\*.cs XKeys\*.cs ZInt\*.cs +echo "TestCrypto..." +%SystemRoot%\Microsoft.NET\Framework\v4.0.30319\csc.exe /target:exe /out:TestCrypto.exe /main:TestCrypto Tests\*.cs Asn1\*.cs Crypto\*.cs SSLTLS\*.cs X500\*.cs XKeys\*.cs ZInt\*.cs + +echo "Client..." +%SystemRoot%\Microsoft.NET\Framework\v4.0.30319\csc.exe /target:exe /out:Client.exe /main:Client Asn1\*.cs Crypto\*.cs IO\*.cs SSLTLS\*.cs X500\*.cs XKeys\*.cs CLI\Client.cs + +echo "Twrch..." +%SystemRoot%\Microsoft.NET\Framework\v4.0.30319\csc.exe /target:exe /out:Twrch.exe /main:Twrch Asn1\*.cs Crypto\*.cs IO\*.cs SSLTLS\*.cs X500\*.cs XKeys\*.cs Twrch\*.cs + diff --git a/build.sh b/build.sh index f54ab99..a52555e 100755 --- a/build.sh +++ b/build.sh @@ -12,11 +12,11 @@ set -e echo "TestCrypto..." $CSC /out:TestCrypto.exe /main:TestCrypto Tests/*.cs Asn1/*.cs Crypto/*.cs SSLTLS/*.cs X500/*.cs XKeys/*.cs ZInt/*.cs -#echo "Client..." -#$CSC /out:Client.exe /main:Client Asn1/*.cs Crypto/*.cs SSLTLS/*.cs X500/*.cs XKeys/*.cs Client.cs +echo "Client..." +$CSC /out:Client.exe /main:Client Asn1/*.cs Crypto/*.cs IO/*.cs SSLTLS/*.cs X500/*.cs XKeys/*.cs CLI/Client.cs #echo "Server..." #$CSC /out:Server.exe /main:Server Asn1/*.cs Crypto/*.cs SSLTLS/*.cs X500/*.cs XKeys/*.cs Server.cs echo "Twrch..." -$CSC /out:Twrch.exe /main:Twrch Asn1/*.cs Crypto/*.cs SSLTLS/*.cs X500/*.cs XKeys/*.cs Twrch/*.cs +$CSC /out:Twrch.exe /main:Twrch Asn1/*.cs Crypto/*.cs IO/*.cs SSLTLS/*.cs X500/*.cs XKeys/*.cs Twrch/*.cs -- 2.17.1