Initial commit.
[BoarSSL] / Twrch / Twrch.cs
1 /*
2 * Copyright (c) 2017 Thomas Pornin <pornin@bolet.org>
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining
5 * a copy of this software and associated documentation files (the
6 * "Software"), to deal in the Software without restriction, including
7 * without limitation the rights to use, copy, modify, merge, publish,
8 * distribute, sublicense, and/or sell copies of the Software, and to
9 * permit persons to whom the Software is furnished to do so, subject to
10 * the following conditions:
11 *
12 * The above copyright notice and this permission notice shall be
13 * included in all copies or substantial portions of the Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
19 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
20 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
21 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 * SOFTWARE.
23 */
24
25 using System;
26 using System.Collections.Generic;
27 using System.Diagnostics;
28 using System.IO;
29 using System.Text;
30
31 using Asn1;
32 using Crypto;
33 using SSLTLS;
34 using XKeys;
35
36 /*
37 * This is the main Twrch class implementation: it provides the entry
38 * point for the command-line application.
39 */
40
41 public class Twrch {
42
43 public static void Main(string[] args)
44 {
45 try {
46 new Twrch().Run(args);
47 } catch (Exception e) {
48 Console.WriteLine(e.ToString());
49 Environment.Exit(1);
50 }
51 }
52
53 bool trace;
54 object conf;
55 string commandFile;
56 string commandArgs;
57 bool commandVerbose;
58 string chainRSAFile;
59 byte[][] chainRSA;
60 string skeyRSAFile;
61 IPrivateKey skeyRSA;
62 string chainECFile;
63 byte[][] chainEC;
64 string skeyECFile;
65 IPrivateKey skeyEC;
66 int[] versions;
67 int versionMin;
68 int versionMax;
69 int[] cipherSuites;
70 int[] hashAndSigns;
71 int[] curves;
72 bool noCloseNotify;
73 object[] tests;
74 IDictionary<string, object> testsByName;
75
76 int totalTests;
77 int totalSuccess;
78 int totalFailures;
79
80 void Run(string[] args)
81 {
82 List<string> r = new List<string>();
83 string confName = null;
84 int doEnum = 0;
85 foreach (string a in args) {
86 string b = a.ToLowerInvariant();
87 switch (b) {
88 case "-trace":
89 trace = true;
90 break;
91 case "-enum":
92 doEnum = 1;
93 break;
94 case "-noenum":
95 doEnum = -1;
96 break;
97 case "-cv":
98 commandVerbose = true;
99 break;
100 default:
101 if (confName == null) {
102 confName = a;
103 } else {
104 r.Add(a);
105 }
106 break;
107 }
108 }
109 if (confName == null) {
110 Usage();
111 }
112 string[] testNames = r.ToArray();
113 conf = ReadConfig(confName);
114 if (doEnum == 0) {
115 doEnum = (testNames.Length == 0) ? 1 : -1;
116 }
117 commandFile = JSON.GetString(conf, "commandFile");
118 commandArgs = JSON.GetString(conf, "commandArgs");
119 chainRSAFile = JSON.GetString(conf, "chainRSA");
120 chainECFile = JSON.GetString(conf, "chainEC");
121 skeyRSAFile = JSON.GetString(conf, "skeyRSA");
122 skeyECFile = JSON.GetString(conf, "skeyEC");
123 chainRSA = DecodeChain(chainRSAFile);
124 skeyRSA = DecodePrivateKey(skeyRSAFile);
125 chainEC = DecodeChain(chainECFile);
126 skeyEC = DecodePrivateKey(skeyECFile);
127 versions = GetVersions();
128 if (versions.Length == 0) {
129 throw new Exception("Bad config: no versions");
130 }
131 versionMin = Int32.MaxValue;
132 versionMax = -1;
133 foreach (int v in versions) {
134 versionMin = Math.Min(v, versionMin);
135 versionMax = Math.Max(v, versionMax);
136 }
137 cipherSuites = GetCipherSuites();
138 if (cipherSuites.Length == 0) {
139 throw new Exception("Bad config: no cipher suites");
140 }
141 hashAndSigns = GetHashAndSigns();
142 if (hashAndSigns.Length == 0) {
143 throw new Exception("Bad config: no hash-and-signs");
144 }
145 curves = GetCurves();
146 noCloseNotify = JSON.GetBool(conf, "noCloseNotify");
147 tests = JSON.GetArray(conf, "tests");
148 testsByName = new SortedDictionary<string, object>(
149 StringComparer.Ordinal);
150 foreach (object obj in tests) {
151 string name = JSON.GetString(obj, "name");
152 testsByName[name] = obj;
153 }
154
155 totalTests = 0;
156 totalSuccess = 0;
157 totalFailures = 0;
158 if (doEnum > 0) {
159 totalTests += ComputeTotalEnum();
160 }
161 if (testNames.Length == 0) {
162 foreach (object obj in tests) {
163 totalTests += GetNumTests(obj);
164 }
165 } else {
166 foreach (string name in testNames) {
167 bool client;
168 int version, suite, curve, hs;
169 if (StringToTEnum(name, out client, out version,
170 out suite, out curve, out hs))
171 {
172 totalTests ++;
173 continue;
174 }
175 if (name.EndsWith("_client")
176 || name.EndsWith("_server"))
177 {
178 totalTests ++;
179 } else {
180 totalTests += GetNumTests(
181 testsByName[name]);
182 }
183 }
184 }
185
186 if (doEnum > 0) {
187 RunEnum();
188 }
189 if (testNames.Length == 0) {
190 foreach (object obj in tests) {
191 RunTest(obj);
192 }
193 } else {
194 foreach (string name in testNames) {
195 bool client;
196 int version, suite, curve, hs;
197 if (StringToTEnum(name, out client, out version,
198 out suite, out curve, out hs))
199 {
200 RunEnum(client, version,
201 suite, curve, hs);
202 continue;
203 }
204 if (name.EndsWith("_client")) {
205 client = true;
206 } else if (name.EndsWith("_server")) {
207 client = false;
208 } else {
209 RunTest(testsByName[name]);
210 continue;
211 }
212 string s = name.Substring(0, name.Length - 7);
213 RunTest(client, testsByName[s]);
214 }
215 }
216
217 Console.WriteLine();
218 Console.WriteLine("\rtotal = {0}, failed = {1}",
219 totalTests, totalFailures);
220 }
221
222 static void Usage()
223 {
224 Console.WriteLine(
225 "usage: Twrch.exe [ options ] config [ test... ]");
226 Console.WriteLine(
227 "options:");
228 Console.WriteLine(
229 " -trace enable trace mode (hex dump of all exchanged bytes)");
230 Console.WriteLine(
231 " -cv pass the '-v' argument to the test command");
232 Console.WriteLine(
233 " -enum perform all version/suite/curve/hash&sign combination tests");
234 Console.WriteLine(
235 " -noenum do NOT perform the version/suite/curve/hash&sign tests");
236 Environment.Exit(1);
237 }
238
239 static object ReadConfig(string fname)
240 {
241 using (TextReader r = File.OpenText(fname)) {
242 return JSON.Parse(r);
243 }
244 }
245
246 int[] GetVersions()
247 {
248 string[] r = JSON.GetStringArray(conf, "versions");
249 int[] vv = new int[r.Length];
250 for (int i = 0; i < r.Length; i ++) {
251 vv[i] = GetVersionByName(r[i]);
252 }
253 return vv;
254 }
255
256 internal static int GetVersionByName(string s)
257 {
258 s = s.Replace(" ", "").Replace(".", "").ToUpperInvariant();
259 switch (s) {
260 case "TLS10": return SSL.TLS10;
261 case "TLS11": return SSL.TLS11;
262 case "TLS12": return SSL.TLS12;
263 default:
264 throw new Exception(string.Format(
265 "Unknown version: '{0}'", s));
266 }
267 }
268
269 int[] GetCipherSuites()
270 {
271 return GetSuitesByName(
272 JSON.GetStringArray(conf, "cipherSuites"));
273 }
274
275 internal static int[] GetSuitesByName(string[] ss)
276 {
277 int[] r = new int[ss.Length];
278 for (int i = 0; i < ss.Length; i ++) {
279 r[i] = GetSuiteByName(ss[i]);
280 }
281 return r;
282 }
283
284 internal static int GetSuiteByName(string s)
285 {
286 switch (s) {
287 case "NULL_WITH_NULL_NULL":
288 return SSL.NULL_WITH_NULL_NULL;
289 case "RSA_WITH_NULL_MD5":
290 return SSL.RSA_WITH_NULL_MD5;
291 case "RSA_WITH_NULL_SHA":
292 return SSL.RSA_WITH_NULL_SHA;
293 case "RSA_WITH_NULL_SHA256":
294 return SSL.RSA_WITH_NULL_SHA256;
295 case "RSA_WITH_RC4_128_MD5":
296 return SSL.RSA_WITH_RC4_128_MD5;
297 case "RSA_WITH_RC4_128_SHA":
298 return SSL.RSA_WITH_RC4_128_SHA;
299 case "RSA_WITH_3DES_EDE_CBC_SHA":
300 return SSL.RSA_WITH_3DES_EDE_CBC_SHA;
301 case "RSA_WITH_AES_128_CBC_SHA":
302 return SSL.RSA_WITH_AES_128_CBC_SHA;
303 case "RSA_WITH_AES_256_CBC_SHA":
304 return SSL.RSA_WITH_AES_256_CBC_SHA;
305 case "RSA_WITH_AES_128_CBC_SHA256":
306 return SSL.RSA_WITH_AES_128_CBC_SHA256;
307 case "RSA_WITH_AES_256_CBC_SHA256":
308 return SSL.RSA_WITH_AES_256_CBC_SHA256;
309 case "DH_DSS_WITH_3DES_EDE_CBC_SHA":
310 return SSL.DH_DSS_WITH_3DES_EDE_CBC_SHA;
311 case "DH_RSA_WITH_3DES_EDE_CBC_SHA":
312 return SSL.DH_RSA_WITH_3DES_EDE_CBC_SHA;
313 case "DHE_DSS_WITH_3DES_EDE_CBC_SHA":
314 return SSL.DHE_DSS_WITH_3DES_EDE_CBC_SHA;
315 case "DHE_RSA_WITH_3DES_EDE_CBC_SHA":
316 return SSL.DHE_RSA_WITH_3DES_EDE_CBC_SHA;
317 case "DH_DSS_WITH_AES_128_CBC_SHA":
318 return SSL.DH_DSS_WITH_AES_128_CBC_SHA;
319 case "DH_RSA_WITH_AES_128_CBC_SHA":
320 return SSL.DH_RSA_WITH_AES_128_CBC_SHA;
321 case "DHE_DSS_WITH_AES_128_CBC_SHA":
322 return SSL.DHE_DSS_WITH_AES_128_CBC_SHA;
323 case "DHE_RSA_WITH_AES_128_CBC_SHA":
324 return SSL.DHE_RSA_WITH_AES_128_CBC_SHA;
325 case "DH_DSS_WITH_AES_256_CBC_SHA":
326 return SSL.DH_DSS_WITH_AES_256_CBC_SHA;
327 case "DH_RSA_WITH_AES_256_CBC_SHA":
328 return SSL.DH_RSA_WITH_AES_256_CBC_SHA;
329 case "DHE_DSS_WITH_AES_256_CBC_SHA":
330 return SSL.DHE_DSS_WITH_AES_256_CBC_SHA;
331 case "DHE_RSA_WITH_AES_256_CBC_SHA":
332 return SSL.DHE_RSA_WITH_AES_256_CBC_SHA;
333 case "DH_DSS_WITH_AES_128_CBC_SHA256":
334 return SSL.DH_DSS_WITH_AES_128_CBC_SHA256;
335 case "DH_RSA_WITH_AES_128_CBC_SHA256":
336 return SSL.DH_RSA_WITH_AES_128_CBC_SHA256;
337 case "DHE_DSS_WITH_AES_128_CBC_SHA256":
338 return SSL.DHE_DSS_WITH_AES_128_CBC_SHA256;
339 case "DHE_RSA_WITH_AES_128_CBC_SHA256":
340 return SSL.DHE_RSA_WITH_AES_128_CBC_SHA256;
341 case "DH_DSS_WITH_AES_256_CBC_SHA256":
342 return SSL.DH_DSS_WITH_AES_256_CBC_SHA256;
343 case "DH_RSA_WITH_AES_256_CBC_SHA256":
344 return SSL.DH_RSA_WITH_AES_256_CBC_SHA256;
345 case "DHE_DSS_WITH_AES_256_CBC_SHA256":
346 return SSL.DHE_DSS_WITH_AES_256_CBC_SHA256;
347 case "DHE_RSA_WITH_AES_256_CBC_SHA256":
348 return SSL.DHE_RSA_WITH_AES_256_CBC_SHA256;
349 case "DH_anon_WITH_RC4_128_MD5":
350 return SSL.DH_anon_WITH_RC4_128_MD5;
351 case "DH_anon_WITH_3DES_EDE_CBC_SHA":
352 return SSL.DH_anon_WITH_3DES_EDE_CBC_SHA;
353 case "DH_anon_WITH_AES_128_CBC_SHA":
354 return SSL.DH_anon_WITH_AES_128_CBC_SHA;
355 case "DH_anon_WITH_AES_256_CBC_SHA":
356 return SSL.DH_anon_WITH_AES_256_CBC_SHA;
357 case "DH_anon_WITH_AES_128_CBC_SHA256":
358 return SSL.DH_anon_WITH_AES_128_CBC_SHA256;
359 case "DH_anon_WITH_AES_256_CBC_SHA256":
360 return SSL.DH_anon_WITH_AES_256_CBC_SHA256;
361 case "ECDH_ECDSA_WITH_NULL_SHA":
362 return SSL.ECDH_ECDSA_WITH_NULL_SHA;
363 case "ECDH_ECDSA_WITH_RC4_128_SHA":
364 return SSL.ECDH_ECDSA_WITH_RC4_128_SHA;
365 case "ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA":
366 return SSL.ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA;
367 case "ECDH_ECDSA_WITH_AES_128_CBC_SHA":
368 return SSL.ECDH_ECDSA_WITH_AES_128_CBC_SHA;
369 case "ECDH_ECDSA_WITH_AES_256_CBC_SHA":
370 return SSL.ECDH_ECDSA_WITH_AES_256_CBC_SHA;
371 case "ECDHE_ECDSA_WITH_NULL_SHA":
372 return SSL.ECDHE_ECDSA_WITH_NULL_SHA;
373 case "ECDHE_ECDSA_WITH_RC4_128_SHA":
374 return SSL.ECDHE_ECDSA_WITH_RC4_128_SHA;
375 case "ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA":
376 return SSL.ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA;
377 case "ECDHE_ECDSA_WITH_AES_128_CBC_SHA":
378 return SSL.ECDHE_ECDSA_WITH_AES_128_CBC_SHA;
379 case "ECDHE_ECDSA_WITH_AES_256_CBC_SHA":
380 return SSL.ECDHE_ECDSA_WITH_AES_256_CBC_SHA;
381 case "ECDH_RSA_WITH_NULL_SHA":
382 return SSL.ECDH_RSA_WITH_NULL_SHA;
383 case "ECDH_RSA_WITH_RC4_128_SHA":
384 return SSL.ECDH_RSA_WITH_RC4_128_SHA;
385 case "ECDH_RSA_WITH_3DES_EDE_CBC_SHA":
386 return SSL.ECDH_RSA_WITH_3DES_EDE_CBC_SHA;
387 case "ECDH_RSA_WITH_AES_128_CBC_SHA":
388 return SSL.ECDH_RSA_WITH_AES_128_CBC_SHA;
389 case "ECDH_RSA_WITH_AES_256_CBC_SHA":
390 return SSL.ECDH_RSA_WITH_AES_256_CBC_SHA;
391 case "ECDHE_RSA_WITH_NULL_SHA":
392 return SSL.ECDHE_RSA_WITH_NULL_SHA;
393 case "ECDHE_RSA_WITH_RC4_128_SHA":
394 return SSL.ECDHE_RSA_WITH_RC4_128_SHA;
395 case "ECDHE_RSA_WITH_3DES_EDE_CBC_SHA":
396 return SSL.ECDHE_RSA_WITH_3DES_EDE_CBC_SHA;
397 case "ECDHE_RSA_WITH_AES_128_CBC_SHA":
398 return SSL.ECDHE_RSA_WITH_AES_128_CBC_SHA;
399 case "ECDHE_RSA_WITH_AES_256_CBC_SHA":
400 return SSL.ECDHE_RSA_WITH_AES_256_CBC_SHA;
401 case "ECDH_anon_WITH_NULL_SHA":
402 return SSL.ECDH_anon_WITH_NULL_SHA;
403 case "ECDH_anon_WITH_RC4_128_SHA":
404 return SSL.ECDH_anon_WITH_RC4_128_SHA;
405 case "ECDH_anon_WITH_3DES_EDE_CBC_SHA":
406 return SSL.ECDH_anon_WITH_3DES_EDE_CBC_SHA;
407 case "ECDH_anon_WITH_AES_128_CBC_SHA":
408 return SSL.ECDH_anon_WITH_AES_128_CBC_SHA;
409 case "ECDH_anon_WITH_AES_256_CBC_SHA":
410 return SSL.ECDH_anon_WITH_AES_256_CBC_SHA;
411 case "RSA_WITH_AES_128_GCM_SHA256":
412 return SSL.RSA_WITH_AES_128_GCM_SHA256;
413 case "RSA_WITH_AES_256_GCM_SHA384":
414 return SSL.RSA_WITH_AES_256_GCM_SHA384;
415 case "DHE_RSA_WITH_AES_128_GCM_SHA256":
416 return SSL.DHE_RSA_WITH_AES_128_GCM_SHA256;
417 case "DHE_RSA_WITH_AES_256_GCM_SHA384":
418 return SSL.DHE_RSA_WITH_AES_256_GCM_SHA384;
419 case "DH_RSA_WITH_AES_128_GCM_SHA256":
420 return SSL.DH_RSA_WITH_AES_128_GCM_SHA256;
421 case "DH_RSA_WITH_AES_256_GCM_SHA384":
422 return SSL.DH_RSA_WITH_AES_256_GCM_SHA384;
423 case "DHE_DSS_WITH_AES_128_GCM_SHA256":
424 return SSL.DHE_DSS_WITH_AES_128_GCM_SHA256;
425 case "DHE_DSS_WITH_AES_256_GCM_SHA384":
426 return SSL.DHE_DSS_WITH_AES_256_GCM_SHA384;
427 case "DH_DSS_WITH_AES_128_GCM_SHA256":
428 return SSL.DH_DSS_WITH_AES_128_GCM_SHA256;
429 case "DH_DSS_WITH_AES_256_GCM_SHA384":
430 return SSL.DH_DSS_WITH_AES_256_GCM_SHA384;
431 case "DH_anon_WITH_AES_128_GCM_SHA256":
432 return SSL.DH_anon_WITH_AES_128_GCM_SHA256;
433 case "DH_anon_WITH_AES_256_GCM_SHA384":
434 return SSL.DH_anon_WITH_AES_256_GCM_SHA384;
435 case "ECDHE_ECDSA_WITH_AES_128_CBC_SHA256":
436 return SSL.ECDHE_ECDSA_WITH_AES_128_CBC_SHA256;
437 case "ECDHE_ECDSA_WITH_AES_256_CBC_SHA384":
438 return SSL.ECDHE_ECDSA_WITH_AES_256_CBC_SHA384;
439 case "ECDH_ECDSA_WITH_AES_128_CBC_SHA256":
440 return SSL.ECDH_ECDSA_WITH_AES_128_CBC_SHA256;
441 case "ECDH_ECDSA_WITH_AES_256_CBC_SHA384":
442 return SSL.ECDH_ECDSA_WITH_AES_256_CBC_SHA384;
443 case "ECDHE_RSA_WITH_AES_128_CBC_SHA256":
444 return SSL.ECDHE_RSA_WITH_AES_128_CBC_SHA256;
445 case "ECDHE_RSA_WITH_AES_256_CBC_SHA384":
446 return SSL.ECDHE_RSA_WITH_AES_256_CBC_SHA384;
447 case "ECDH_RSA_WITH_AES_128_CBC_SHA256":
448 return SSL.ECDH_RSA_WITH_AES_128_CBC_SHA256;
449 case "ECDH_RSA_WITH_AES_256_CBC_SHA384":
450 return SSL.ECDH_RSA_WITH_AES_256_CBC_SHA384;
451 case "ECDHE_ECDSA_WITH_AES_128_GCM_SHA256":
452 return SSL.ECDHE_ECDSA_WITH_AES_128_GCM_SHA256;
453 case "ECDHE_ECDSA_WITH_AES_256_GCM_SHA384":
454 return SSL.ECDHE_ECDSA_WITH_AES_256_GCM_SHA384;
455 case "ECDH_ECDSA_WITH_AES_128_GCM_SHA256":
456 return SSL.ECDH_ECDSA_WITH_AES_128_GCM_SHA256;
457 case "ECDH_ECDSA_WITH_AES_256_GCM_SHA384":
458 return SSL.ECDH_ECDSA_WITH_AES_256_GCM_SHA384;
459 case "ECDHE_RSA_WITH_AES_128_GCM_SHA256":
460 return SSL.ECDHE_RSA_WITH_AES_128_GCM_SHA256;
461 case "ECDHE_RSA_WITH_AES_256_GCM_SHA384":
462 return SSL.ECDHE_RSA_WITH_AES_256_GCM_SHA384;
463 case "ECDH_RSA_WITH_AES_128_GCM_SHA256":
464 return SSL.ECDH_RSA_WITH_AES_128_GCM_SHA256;
465 case "ECDH_RSA_WITH_AES_256_GCM_SHA384":
466 return SSL.ECDH_RSA_WITH_AES_256_GCM_SHA384;
467 case "ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256":
468 return SSL.ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256;
469 case "ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256":
470 return SSL.ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256;
471 case "DHE_RSA_WITH_CHACHA20_POLY1305_SHA256":
472 return SSL.DHE_RSA_WITH_CHACHA20_POLY1305_SHA256;
473 case "PSK_WITH_CHACHA20_POLY1305_SHA256":
474 return SSL.PSK_WITH_CHACHA20_POLY1305_SHA256;
475 case "ECDHE_PSK_WITH_CHACHA20_POLY1305_SHA256":
476 return SSL.ECDHE_PSK_WITH_CHACHA20_POLY1305_SHA256;
477 case "DHE_PSK_WITH_CHACHA20_POLY1305_SHA256":
478 return SSL.DHE_PSK_WITH_CHACHA20_POLY1305_SHA256;
479 case "RSA_PSK_WITH_CHACHA20_POLY1305_SHA256":
480 return SSL.RSA_PSK_WITH_CHACHA20_POLY1305_SHA256;
481 default:
482 throw new Exception(string.Format(
483 "Unknown cipher suite: '{0}'", s));
484 }
485 }
486
487 int[] GetHashAndSigns()
488 {
489 return GetHashAndSignsByName(
490 JSON.GetStringArray(conf, "hashAndSigns"));
491 }
492
493 internal static int[] GetHashAndSignsByName(string[] ss)
494 {
495 int[] r = new int[ss.Length];
496 for (int i = 0; i < ss.Length; i ++) {
497 r[i] = GetHashAndSignByName(ss[i]);
498 }
499 return r;
500 }
501
502 internal static int GetHashAndSignByName(string s)
503 {
504 switch (s) {
505 case "RSA_MD5": return SSL.RSA_MD5;
506 case "RSA_SHA1": return SSL.RSA_SHA1;
507 case "RSA_SHA224": return SSL.RSA_SHA224;
508 case "RSA_SHA256": return SSL.RSA_SHA256;
509 case "RSA_SHA384": return SSL.RSA_SHA384;
510 case "RSA_SHA512": return SSL.RSA_SHA512;
511 case "ECDSA_MD5": return SSL.ECDSA_MD5;
512 case "ECDSA_SHA1": return SSL.ECDSA_SHA1;
513 case "ECDSA_SHA224": return SSL.ECDSA_SHA224;
514 case "ECDSA_SHA256": return SSL.ECDSA_SHA256;
515 case "ECDSA_SHA384": return SSL.ECDSA_SHA384;
516 case "ECDSA_SHA512": return SSL.ECDSA_SHA512;
517 default:
518 throw new Exception(string.Format(
519 "Unknown hash-and-sign: '{0}'", s));
520 }
521 }
522
523 int[] GetCurves()
524 {
525 return GetCurvesByName(JSON.GetStringArray(conf, "curves"));
526 }
527
528 internal static int[] GetCurvesByName(string[] ss)
529 {
530 int[] r = new int[ss.Length];
531 for (int i = 0; i < ss.Length; i ++) {
532 r[i] = GetCurveByName(ss[i]);
533 }
534 return r;
535 }
536
537 internal static int GetCurveByName(string s)
538 {
539 switch (s) {
540 case "Curve25519": return SSL.Curve25519;
541 case "NIST_P256": return SSL.NIST_P256;
542 case "NIST_P384": return SSL.NIST_P384;
543 case "NIST_P521": return SSL.NIST_P521;
544 default:
545 throw new Exception(string.Format(
546 "Unknown curve: '{0}'", s));
547 }
548 }
549
550 /*
551 * RunEnum() builds and runs synthetic tests that exercise all
552 * combinations of protocol version, cipher suites, curves
553 * and hash-and-sign. Curves and hash-and-sign are enumerated
554 * only for ECDHE suites.
555 */
556 void RunEnum()
557 {
558 RunEnum(true, true);
559 RunEnum(false, true);
560 }
561
562 int RunEnum(bool cmdClient, bool doit)
563 {
564 int count = 0;
565 foreach (int version in versions) {
566 foreach (int suite in cipherSuites) {
567 if (version < SSL.TLS12 && SSL.IsTLS12(suite)) {
568 continue;
569 }
570 if (!SSL.IsECDHE(suite)) {
571 if (doit) {
572 RunEnum(cmdClient,
573 version, suite, -1, -1);
574 }
575 count ++;
576 continue;
577 }
578 bool needRSA = (version >= SSL.TLS12)
579 && SSL.IsECDHE_RSA(suite);
580 bool needECDSA = (version >= SSL.TLS12)
581 && SSL.IsECDHE_ECDSA(suite);
582 foreach (int hs in hashAndSigns) {
583 int sa = hs & 0xFF;
584 if (needRSA && sa != SSL.RSA) {
585 continue;
586 }
587 if (needECDSA && sa != SSL.ECDSA) {
588 continue;
589 }
590 foreach (int curve in curves) {
591 if (doit) {
592 RunEnum(cmdClient,
593 version,
594 suite,
595 curve, hs);
596 }
597 count ++;
598 }
599 }
600 }
601 }
602 return count;
603 }
604
605 int ComputeTotalEnum()
606 {
607 return RunEnum(true, false) + RunEnum(false, false);
608 }
609
610 static string TEnumToString(
611 int version, int suite, int curve, int hs)
612 {
613 StringBuilder sb = new StringBuilder();
614 sb.AppendFormat("enum_{0:X4}_{1:X4}", version, suite);
615 if (curve >= 0 && hs >= 0) {
616 sb.AppendFormat("_{0}_{1}", curve, hs);
617 }
618 return sb.ToString();
619 }
620
621 static bool StringToTEnum(string s,
622 out bool cmdClient,
623 out int version, out int suite,
624 out int curve, out int hs)
625 {
626 cmdClient = false;
627 version = -1;
628 suite = -1;
629 curve = -1;
630 hs = -1;
631 s = s.Trim();
632 if (!s.StartsWith("enum_")) {
633 return false;
634 }
635 s = s.Substring(5);
636 if (s.EndsWith("_client")) {
637 cmdClient = true;
638 } else if (s.EndsWith("_server")) {
639 cmdClient = false;
640 } else {
641 return false;
642 }
643 s = s.Substring(0, s.Length - 7);
644 string[] ww = s.Split('_');
645 if (ww.Length != 2 && ww.Length != 4) {
646 return false;
647 }
648 version = ParseHex4(ww[0]);
649 suite = ParseHex4(ww[1]);
650 if (ww.Length == 2) {
651 return version >= 0 && suite >= 0;
652 } else {
653 curve = ParseDec(ww[2]);
654 hs = ParseDec(ww[3]);
655 return version >= 0 && suite >= 0
656 && curve >= 0 && hs >= 0;
657 }
658 }
659
660 static int HexVal(int cp)
661 {
662 if (cp >= '0' && cp <= '9') {
663 return cp - '0';
664 } else if (cp >= 'A' && cp <= 'F') {
665 return cp - ('A' - 10);
666 } else if (cp >= 'a' && cp <= 'f') {
667 return cp - ('a' - 10);
668 } else {
669 return -1;
670 }
671 }
672
673 static int ParseHex4(string s)
674 {
675 if (s.Length != 4) {
676 return -1;
677 }
678 return ParseHex(s);
679 }
680
681 static int ParseHex(string s)
682 {
683 int acc = 0;
684 for (int i = 0; i < 4; i ++) {
685 int v = HexVal(s[i]);
686 if (v < 0) {
687 return -1;
688 }
689 acc = (acc << 4) + v;
690 }
691 return acc;
692 }
693
694 static int ParseDec(string s)
695 {
696 int n = s.Length;
697 int acc = 0;
698 for (int i = 0; i < n; i ++) {
699 int v = HexVal(s[i]);
700 if (v < 0 || v >= 10) {
701 return -1;
702 }
703 acc = (acc * 10) + v;
704 }
705 return acc;
706 }
707
708 void RunEnum(bool cmdClient, int version, int suite, int curve, int hs)
709 {
710 IDictionary<string, object> d =
711 new SortedDictionary<string, object>(
712 StringComparer.Ordinal);
713 d["name"] = TEnumToString(version, suite, curve, hs);
714 d["versionMin"] = SSL.VersionName(version);
715 d["versionMax"] = SSL.VersionName(version);
716 d["cipherSuites"] = new string[] {
717 SSL.CipherSuiteName(suite)
718 };
719 if (curve >= 0) {
720 d["curves"] = new string[] {
721 SSL.CurveName(curve)
722 };
723 d["hashAndSigns"] = new string[] {
724 SSL.HashAndSignName(hs)
725 };
726 } else {
727 d["curves"] = new string[0];
728 d["hashAndSigns"] = new string[0];
729 }
730 if (SSL.IsRSA(suite) || SSL.IsECDHE_RSA(suite)) {
731 d["serverCertType"] = "RSA";
732 }
733 if (SSL.IsECDH(suite) || SSL.IsECDHE_ECDSA(suite)) {
734 d["serverCertType"] = "EC";
735 }
736 RunTest(cmdClient, d);
737 }
738
739 /*
740 * Get certificate type for the provided test (client-side or
741 * server-side certificate, depending on 'client').
742 *
743 * If the test does not contain an explicit indication, then
744 * the certificate type will be "none" for a client, "RSA" for
745 * a server.
746 */
747 string GetCertType(object obj, bool client)
748 {
749 string name = client ? "clientCertType" : "serverCertType";
750 string ct;
751 if (JSON.TryGetString(obj, name, out ct)) {
752 return ct;
753 }
754 return client ? "none" : "RSA";
755 }
756
757 int GetNumTests(object obj)
758 {
759 int num = 0;
760 bool v;
761 if (!JSON.TryGetBool(obj, "serverOnly", out v) || !v) {
762 num ++;
763 }
764 if (!JSON.TryGetBool(obj, "clientOnly", out v) || !v) {
765 num ++;
766 }
767 return num;
768 }
769
770 void RunTest(object obj)
771 {
772 bool v;
773 if (!JSON.TryGetBool(obj, "serverOnly", out v) || !v) {
774 RunTest(true, obj);
775 }
776 if (!JSON.TryGetBool(obj, "clientOnly", out v) || !v) {
777 RunTest(false, obj);
778 }
779 }
780
781 void RunTest(bool cmdClient, object obj)
782 {
783 string name = JSON.GetString(obj, "name")
784 + (cmdClient ? "_client" : "_server");
785 Console.Write("\r({0}/{1})",
786 totalSuccess + totalFailures + 1, totalTests);
787 // Console.Write("{0}:", name);
788
789 /*
790 * Expected command exit code:
791 *
792 * 0 if the command is supposed to exit gracefully
793 * 1 if the command should detect and report an error
794 */
795 int expectedExitCode;
796 JSON.TryGetInt32(obj, "expectedExitCode", out expectedExitCode);
797
798 /*
799 * Expected failure: if defined, then we expect our
800 * library to throw an exception, and the message should
801 * contain that specific string.
802 */
803 string expectedFailure;
804 JSON.TryGetString(obj, "expectedFailure", out expectedFailure);
805
806 /*
807 * Assemble the sub-process command line:
808 *
809 * - Always one of "-client" or "-server"
810 * - For a server command, a certificate and key are
811 * always provided (defaults to RSA); for a client,
812 * only if explicitly asked for.
813 */
814 StringBuilder sb = new StringBuilder();
815 if (cmdClient) {
816 sb.Append("-client");
817 } else {
818 sb.Append("-server");
819 }
820 if (commandVerbose) {
821 sb.Append(" -v");
822 }
823 string certType = GetCertType(obj, cmdClient);
824 switch (certType) {
825 case "RSA":
826 sb.AppendFormat(" -cert \"{0}\" -key \"{1}\"",
827 chainRSAFile, skeyRSAFile);
828 break;
829 case "EC":
830 sb.AppendFormat(" -cert \"{0}\" -key \"{1}\"",
831 chainECFile, skeyECFile);
832 break;
833 case "none":
834 break;
835 default:
836 throw new Exception("Unknown certType: " + certType);
837 }
838 string extra;
839 if (JSON.TryGetString(obj, "extraArgs", out extra)) {
840 sb.Append(' ');
841 sb.Append(extra);
842 }
843
844 /*
845 * Run the sub-process.
846 */
847 ProcessStartInfo si = new ProcessStartInfo();
848 si.FileName = commandFile;
849 si.Arguments = string.Format(commandArgs, sb.ToString());
850 si.UseShellExecute = false;
851 si.ErrorDialog = false;
852 si.CreateNoWindow = true;
853 si.RedirectStandardInput = true;
854 si.RedirectStandardOutput = true;
855
856 using (Process pp = new Process()) {
857 pp.StartInfo = si;
858 pp.Start();
859 Exception delayed = null;
860 try {
861 /*
862 * TODO: add a time-out on the streams
863 * so that the test never stalls
864 * indefinitely if the two SSL engines
865 * lose synchronisation.
866 */
867 MergeStream ms = new MergeStream(
868 pp.StandardOutput.BaseStream,
869 pp.StandardInput.BaseStream);
870 if (trace) {
871 ms.Debug = Console.Out;
872 }
873 RunTestInner(cmdClient, obj, ms);
874 } catch (Exception ex) {
875 delayed = ex;
876 }
877
878 /*
879 * Once the test has run, we must make sure that
880 * the sub-processed is finished. It _should_ end
881 * properly by itself for all successful test cases,
882 * so if we have to kill it, then it's a bug.
883 */
884 bool killed = false;
885 if (!pp.WaitForExit(2000)) {
886 try {
887 pp.Kill();
888 } catch {
889 // ignored
890 }
891 pp.WaitForExit();
892 killed = true;
893 }
894 int exc = pp.ExitCode;
895
896 /*
897 * If we had to kill the command, then that is
898 * always a bug. Otherwise, we compare what we
899 * got with the expected outcomes.
900 */
901 List<string> msg = new List<string>();
902 if (killed) {
903 msg.Add("COMMAND KILLED");
904 }
905 if (exc != expectedExitCode) {
906 msg.Add("Wrong exit code: "
907 + exc + " (expected: "
908 + expectedExitCode + ")");
909 }
910 if (delayed == null) {
911 if (expectedFailure != null) {
912 msg.Add("An exception was expected");
913 }
914 } else {
915 if (expectedFailure == null) {
916 msg.Add(delayed.ToString());
917 } else {
918 string s = delayed.Message;
919 if (s == null) {
920 s = "";
921 }
922 if (s.IndexOf(expectedFailure) < 0) {
923 msg.Add(delayed.ToString());
924 }
925 }
926 }
927 if (msg.Count == 0) {
928 totalSuccess ++;
929 } else {
930 Console.WriteLine("{0}: FAIL:", name);
931 foreach (string s in msg) {
932 Console.WriteLine(s);
933 }
934 totalFailures ++;
935 }
936 }
937 }
938
939 void RunTestInner(bool cmdClient, object obj, Stream peer)
940 {
941 /*
942 * Create the SSL engine, and configure it as specified
943 * in the configuration object (with the default
944 * configuration as fallback).
945 */
946
947 SSLEngine eng;
948 byte[][] chain = null;
949 IPrivateKey skey = null;
950 string certType = GetCertType(obj, !cmdClient);
951 switch (certType) {
952 case "RSA":
953 chain = chainRSA;
954 skey = skeyRSA;
955 break;
956 case "EC":
957 chain = chainEC;
958 skey = skeyEC;
959 break;
960 case "none":
961 break;
962 default:
963 throw new Exception("Unknown certType: " + certType);
964 }
965 if (cmdClient) {
966 IServerPolicy spol = new SSLServerPolicyBasic(
967 chain, skey, KeyUsage.EncryptAndSign);
968 SSLServer ss = new SSLServer(peer, spol);
969 ss.SessionCache = new SSLSessionCacheLRU(20);
970 eng = ss;
971 } else {
972 SSLClient sc = new SSLClient(peer);
973 sc.ServerCertValidator =
974 SSLClient.InsecureCertValidator;
975 eng = sc;
976 }
977 eng.NormalizeIOError = true;
978 eng.AutoFlush = false;
979
980 /*
981 * Minimum version.
982 */
983 string svmin;
984 if (JSON.TryGetString(obj, "versionMin", out svmin)) {
985 eng.VersionMin = GetVersionByName(svmin);
986 } else {
987 eng.VersionMin = versionMin;
988 }
989
990 /*
991 * Maximum version.
992 */
993 string svmax;
994 if (JSON.TryGetString(obj, "versionMax", out svmax)) {
995 eng.VersionMax = GetVersionByName(svmax);
996 } else {
997 eng.VersionMax = versionMax;
998 }
999
1000 /*
1001 * Supported cipher suites.
1002 */
1003 string[] sccs;
1004 if (JSON.TryGetStringArray(obj, "cipherSuites", out sccs)) {
1005 eng.SupportedCipherSuites = GetSuitesByName(sccs);
1006 } else {
1007 eng.SupportedCipherSuites = cipherSuites;
1008 }
1009
1010 /*
1011 * Supported hash-and-sign algorithms.
1012 */
1013 string[] shss;
1014 if (JSON.TryGetStringArray(obj, "hashAndSigns", out shss)) {
1015 eng.SupportedHashAndSign = GetHashAndSignsByName(shss);
1016 } else {
1017 eng.SupportedHashAndSign = hashAndSigns;
1018 }
1019
1020 /*
1021 * Supported elliptic curves.
1022 */
1023 string[] secc;
1024 if (JSON.TryGetStringArray(obj, "curves", out secc)) {
1025 eng.SupportedCurves = GetCurvesByName(secc);
1026 } else {
1027 eng.SupportedCurves = curves;
1028 }
1029
1030 /*
1031 * What to do when there is no close_notify.
1032 */
1033 bool ncn;
1034 if (JSON.TryGetBool(obj, "noCloseNotify", out ncn)) {
1035 eng.NoCloseNotify = ncn;
1036 } else {
1037 eng.NoCloseNotify = noCloseNotify;
1038 }
1039
1040 /*
1041 * Quirks.
1042 */
1043 IDictionary<string, object> qm;
1044 if (JSON.TryGetObjectMap(obj, "quirks", out qm)) {
1045 SSLQuirks q = new SSLQuirks();
1046 foreach (string name in qm.Keys) {
1047 q[name] = JSON.GetString(qm, name);
1048 }
1049 eng.Quirks = q;
1050 }
1051
1052 bool askClose;
1053 JSON.TryGetBool(obj, "askClose", out askClose);
1054 bool renegotiate, renegotiateAccepted;
1055 renegotiate = JSON.TryGetBool(obj, "renegotiate",
1056 out renegotiateAccepted);
1057 bool askRenegotiate, askRenegotiateAccepted;
1058 askRenegotiate = JSON.TryGetBool(obj, "askRenegotiate",
1059 out askRenegotiateAccepted);
1060
1061 bool reconnectSelf = false, reconnectPeer = false;
1062 string rcs;
1063 if (JSON.TryGetString(obj, "reconnect", out rcs)) {
1064 switch (rcs) {
1065 case "self": reconnectSelf = true; break;
1066 case "peer": reconnectPeer = true; break;
1067 default:
1068 throw new Exception("Unknown 'reconnect' type: "
1069 + rcs);
1070 }
1071 }
1072
1073 bool forgetSelf = false, forgetPeer = false;
1074 string fgs;
1075 if (JSON.TryGetString(obj, "forget", out fgs)) {
1076 switch (fgs) {
1077 case "self": forgetSelf = true; break;
1078 case "peer": forgetPeer = true; break;
1079 default:
1080 throw new Exception("Unknown 'forget' type: "
1081 + fgs);
1082 }
1083 }
1084
1085 if (askClose) {
1086 SendCommand(eng, 'C');
1087 if (eng.ReadByte() != -1) {
1088 throw new Exception("Peer did not close");
1089 }
1090 } else if (renegotiate) {
1091 SendMessageNormal(eng, 10);
1092 if (eng.Renegotiate()) {
1093 if (!renegotiateAccepted) {
1094 throw new Exception("Renegotiation"
1095 + " should have been rejected");
1096 }
1097 } else {
1098 if (renegotiateAccepted) {
1099 throw new Exception("Renegotiation"
1100 + " should have been accepted");
1101 }
1102 }
1103 SendMessageNormal(eng, 9);
1104 } else if (askRenegotiate) {
1105 SendMessageNormal(eng, 10);
1106 long rc = eng.HandshakeCount;
1107 SendCommand(eng, 'G');
1108 string s = ReadLine(eng);
1109 switch (s) {
1110 case "DENIED":
1111 if (askRenegotiateAccepted) {
1112 throw new Exception("Renegotiation"
1113 + " should have been accepted");
1114 }
1115 break;
1116 case "OK":
1117 if (!askRenegotiateAccepted) {
1118 throw new Exception("Renegotiation"
1119 + " should have been rejected");
1120 }
1121 long nrc = eng.HandshakeCount;
1122 if (nrc != rc + 1) {
1123 throw new Exception(string.Format(
1124 "Wrong handshake count"
1125 + " (old={0}, new={1})",
1126 rc, nrc));
1127 }
1128 break;
1129 default:
1130 throw new Exception(string.Format(
1131 "Unexpected answer string '{0}'", s));
1132 }
1133 SendMessageNormal(eng, 8);
1134 } else if (reconnectSelf || reconnectPeer) {
1135 SendMessageNormal(eng, 50);
1136 SendMessageNormal(eng, 100);
1137 if (forgetPeer) {
1138 SendCommand(eng, 'U');
1139 string s = ReadLine(eng);
1140 if (s != "DONE") {
1141 throw new Exception(string.Format(
1142 "Unexpected answer '{0}'", s));
1143 }
1144 }
1145 eng.CloseSub = false;
1146 if (reconnectPeer) {
1147 SendCommand(eng, 'T');
1148 if (eng.ReadByte() != -1) {
1149 throw new Exception(
1150 "Peer did not close");
1151 }
1152 } else {
1153 SendCommand(eng, 'R');
1154 string s = ReadLine(eng);
1155 if (s != "OK") {
1156 throw new Exception(string.Format(
1157 "Unexpected answer '{0}'", s));
1158 }
1159 eng.Close();
1160 }
1161 SSLEngine eng2;
1162 if (cmdClient) {
1163 IServerPolicy spol = new SSLServerPolicyBasic(
1164 chain, skey, KeyUsage.EncryptAndSign);
1165 SSLServer ss = new SSLServer(peer, spol);
1166 if (forgetSelf) {
1167 ss.SessionCache =
1168 new SSLSessionCacheLRU(20);
1169 } else {
1170 ss.SessionCache =
1171 ((SSLServer)eng).SessionCache;
1172 }
1173 eng2 = ss;
1174 } else {
1175 SSLSessionParameters sp;
1176 if (forgetSelf) {
1177 sp = null;
1178 } else {
1179 sp = eng.SessionParameters;
1180 }
1181 SSLClient sc = new SSLClient(peer, sp);
1182 sc.ServerCertValidator =
1183 SSLClient.InsecureCertValidator;
1184 eng2 = sc;
1185 }
1186 eng2.NormalizeIOError = eng.NormalizeIOError;
1187 eng2.AutoFlush = eng.AutoFlush;
1188 eng2.VersionMin = eng.VersionMin;
1189 eng2.VersionMax = eng.VersionMax;
1190 eng2.SupportedCipherSuites = eng.SupportedCipherSuites;
1191 eng2.SupportedHashAndSign = eng.SupportedHashAndSign;
1192 eng2.SupportedCurves = eng.SupportedCurves;
1193 eng2.NoCloseNotify = eng.NoCloseNotify;
1194 eng2.Quirks = eng.Quirks;
1195 eng = eng2;
1196 SendMessageNormal(eng, 60);
1197 SendMessageNormal(eng, 90);
1198 if (forgetSelf || forgetPeer) {
1199 if (eng.IsResume) {
1200 throw new Exception(
1201 "Session was resumed");
1202 }
1203 } else {
1204 if (!eng.IsResume) {
1205 throw new Exception(
1206 "Session was not resumed");
1207 }
1208 }
1209 } else {
1210 for (int i = 0; i <= 38; i ++) {
1211 int len;
1212 if (i <= 20) {
1213 len = i;
1214 } else {
1215 len = 20 + (1 << (i - 20));
1216 }
1217 SendMessageNormal(eng, len);
1218 }
1219 }
1220
1221 eng.Close();
1222 }
1223
1224 /*
1225 * Send a "normal" message to the peer, of the specified
1226 * length: this is a sequence of 'len' random bytes, distinct
1227 * from 0x0A, followed one 0x0A byte. The peer is supposed to
1228 * respond with the SHA-1 hash of the message bytes (excluding
1229 * the final 0x0A), encoded in hexadecimal (lowercase) and
1230 * followed by a newline (0x0A). An exception is thrown if the
1231 * expected value is not obtained.
1232 */
1233 void SendMessageNormal(SSLEngine eng, int len)
1234 {
1235 SHA1 sha1 = new SHA1();
1236 byte[] buf = new byte[len + 1];
1237 RNG.GetBytesNonZero(buf, 0, len);
1238 for (int i = 0; i < len; i ++) {
1239 buf[i] ^= 0x0A;
1240 }
1241 buf[len] = 0x0A;
1242 if (len == 1) {
1243 buf[0] = (byte)('a' + (buf[0] & 0x0F));
1244 }
1245 StringBuilder sb = new StringBuilder();
1246 foreach (byte b in sha1.Hash(buf, 0, len)) {
1247 sb.AppendFormat("{0:x2}", b);
1248 }
1249 sb.Append('\n');
1250 eng.Write(buf, 0, buf.Length);
1251 eng.Flush();
1252 for (int i = 0; i < sb.Length; i ++) {
1253 int x = eng.ReadByte();
1254 int y = sb[i];
1255 if (x != y) {
1256 throw new Exception(string.Format(
1257 "received {0} (exp: {1})", y, x));
1258 }
1259 }
1260 }
1261
1262 void SendCommand(SSLEngine eng, char cmd)
1263 {
1264 eng.WriteByte((byte)cmd);
1265 eng.WriteByte(0x0A);
1266 eng.Flush();
1267 }
1268
1269 string ReadLine(SSLEngine eng)
1270 {
1271 StringBuilder sb = new StringBuilder();
1272 for (;;) {
1273 int c = eng.ReadByte();
1274 if (c < 0) {
1275 throw new Exception("Unexpected EOF");
1276 }
1277 if (c == 0x0A) {
1278 return sb.ToString();
1279 }
1280 sb.Append((char)c);
1281 }
1282 }
1283
1284 static byte[][] DecodeChain(string fname)
1285 {
1286 byte[] buf = File.ReadAllBytes(fname);
1287 PEMObject[] fpo = AsnIO.DecodePEM(buf);
1288 if (fpo.Length == 0) {
1289 buf = AsnIO.FindBER(buf);
1290 if (buf == null) {
1291 throw new Exception(string.Format(
1292 "No certificate in file '{0}'", fname));
1293 }
1294 return new byte[][] { buf };
1295 }
1296 List<byte[]> r = new List<byte[]>();
1297 foreach (PEMObject po in fpo) {
1298 string tt = po.type.ToUpperInvariant();
1299 if (tt == "CERTIFICATE" || tt == "X509 CERTIFICATE") {
1300 r.Add(po.data);
1301 }
1302 }
1303 if (r.Count == 0) {
1304 throw new Exception(string.Format(
1305 "No certificate in file '{0}'", fname));
1306 }
1307 return r.ToArray();
1308 }
1309
1310 static IPrivateKey DecodePrivateKey(string fname)
1311 {
1312 byte[] buf = File.ReadAllBytes(fname);
1313 PEMObject[] fpo = AsnIO.DecodePEM(buf);
1314 if (fpo.Length == 0) {
1315 buf = AsnIO.FindBER(buf);
1316 } else {
1317 buf = null;
1318 foreach (PEMObject po in fpo) {
1319 string tt = po.type.ToUpperInvariant();
1320 if (tt.IndexOf("PRIVATE KEY") >= 0) {
1321 if (buf != null) {
1322 throw new Exception(
1323 "Multiple keys in '"
1324 + fname + "'");
1325 }
1326 buf = po.data;
1327 }
1328 }
1329 }
1330 if (buf == null) {
1331 throw new Exception(string.Format(
1332 "No private key in file '{0}'", fname));
1333 }
1334 return KF.DecodePrivateKey(buf);
1335 }
1336 }