Setup TPM2-Plugin build environment
[aaf/sshsm.git] / SoftHSMv2 / win32 / Configure.py
1 #!python
2
3 # Configure -- python version
4 #
5 # this script builds Visual Studio files
6
7 from __future__ import print_function
8
9 import sys
10 import os
11 import os.path
12 import re
13 import subprocess
14
15 # files to configure
16
17 filelist = ["config.h",
18             "softhsm2.sln",
19             "convarch\\convarch.vcxproj.filters",
20             "convarch\\convarch.vcxproj",
21             "cryptotest\\cryptotest.vcxproj",
22             "datamgrtest\\datamgrtest.vcxproj",
23             "dump\\dump.vcxproj",
24             "handlemgrtest\\handlemgrtest.vcxproj",
25             "keyconv\\keyconv.vcxproj.filters",
26             "keyconv\\keyconv.vcxproj",
27             "objstoretest\\objstoretest.vcxproj",
28             "p11test\\p11test.vcxproj",
29             "sessionmgrtest\\sessionmgrtest.vcxproj",
30             "slotmgrtest\\slotmgrtest.vcxproj",
31             "softhsm2\\softhsm2.vcxproj",
32             "util\\util.vcxproj.filters",
33             "util\\util.vcxproj"]
34
35 # test files
36 testlist = ["botan",
37             "ecc",
38             "gnump",
39             "gost",
40             "ossl",
41             "osslv",
42             "rawpss",
43             "rfc3394",
44             "rfc5649"]
45
46 # variables to expand
47
48 varvals = {}
49
50 varnames = ["CUINCPATH",
51             "CULIBPATH",
52             "DEBUGDLLPATH",
53             "DEBUGINCPATH",
54             "DEBUGLIBPATH",
55             "DLLPATH",
56             "EXTRALIBS",
57             "INCLUDEPATH",
58             "LIBNAME",
59             "LIBPATH",
60             "PLATFORM",
61             "PLATFORMDIR",
62             "PLATFORMTOOLSET",
63             "RUNTIMELIBRARY"]
64
65 # conditions to stack
66
67 condvals = {}
68
69 condnames = ["AESGCM",
70              "BOTAN",
71              "ECC",
72              "GOST",
73              "NONPAGE",
74              "OPENSSL",
75              "RAWPSS",
76              "RFC3394",
77              "RFC5649",
78              "TESTS"]
79
80 # enable-xxx/disable-xxx arguments
81
82 enablelist = ["64bit",
83               "debug",
84               "ecc",
85               "gost",
86               "keep",
87               "non-paged-memory",
88               "static-runtime",
89               "verbose"]
90
91 # with-xxx/without-xxx arguments
92
93 withlist = ["botan",
94             "cppunit",
95             "crypto-backend",
96             "debug-botan",
97             "debug-openssl",
98             "openssl",
99             "toolset"]
100
101 # general commands
102
103 commandlist = ["help", "clean"] # verbose, keep
104
105 # usage
106
107 usage = ["Usage: python Configure.py help",
108          "       python Configure.py options*",
109          "       python Configure.py clean"]
110
111 # help
112
113 myhelp = ["'python Configure.py' configures SoftHSMv2 build files.\n"] +\
114 usage + [\
115 "\nGeneral Commands:",
116 "  help                     print this help",
117 "  clean                    clean up generated files",
118 "  <none>                   print a summary of the configuration",
119 "\nOptional Features:",
120 "  enable-verbose           print messages [default=no]",
121 "  enable-keep              keep test files after config [default=no]",
122 "  enable-64bit             enable 64-bit compiling [default=no]",
123 "  enable-debug             enable build of Debug config [default=yes]",
124 "  enable-ecc               enable support for ECC [default=yes]",
125 "  enable-gost              enable support for GOST [default=yes]",
126 "  enable-static-runtime    enable build with static CRT (/MT) [default=no]",
127 "  enable-non-paged-memory  enable non-paged memory [default=yes]",
128 "\nOptional Packages:",
129 "  with-crypto-backend      select the crypto backend [openssl|botan]",
130 "  with-botan=PATH          speficy prefix of path of Botan (Release)",
131 "  with-debug-botan=PATH    speficy prefix of path of Botan (Debug)",
132 "  with-openssl=PATH        speficy prefix of path of OpenSSL (Release)",
133 "  with-debug-openssl=PATH  speficy prefix of path of OpenSSL (Debug)",
134 "  with-cppunit=PATH        specify prefix of path of CppUnit",
135 "  with-toolset=VALUE       set Visual Studio platform toolset version (eg v110 for vs2012)",
136 ]
137
138 # variables for parsing
139
140 verbose = False
141 configargs = None
142 want_help = False
143 want_clean = False
144 want_unknown = False
145 unknown_value = None
146 enable_keep = False
147 enable_debug = True
148 enable_ecc = True
149 enable_gost = True
150 enable_static_runtime = False
151 enable_non_paged = True
152 platform = 32
153 crypto_backend = "openssl"
154 botan_path = "..\\..\\btn"
155 debug_botan_path = None
156 openssl_path = "..\\..\\ssl"
157 debug_openssl_path = None
158 want_tests = True
159 cppunit_path = "..\\..\\cu"
160 toolset = ""
161
162 def dodetectplatform(visualstudio):
163     # detect platform tool set >= VS2010
164     global toolset
165
166     if "Microsoft Visual Studio 10.0" in visualstudio:
167         toolset="v100"
168     elif "Microsoft Visual Studio 11.0" in visualstudio:
169         toolset="v110"
170     elif "Microsoft Visual Studio 12.0" in visualstudio:
171         toolset="v120"
172     elif "Microsoft Visual Studio 14.0" in visualstudio:
173         toolset="v140"
174     else:
175         print("PlatformToolset for \""+visualstudio+"\" not supported")
176         toolset=""
177
178 def dodetectvisualstudio():
179     """detect visual studio version"""
180     if os.environ.get('VSINSTALLDIR'):
181         dodetectplatform(os.environ.get('VSINSTALLDIR'))
182
183 def parseargs(args):
184     """parse arguments"""
185     global verbose
186     global enable_keep
187     global want_help
188     global want_clean
189     global want_unknown
190     global unknown_value
191     global debug_botan_path
192     global debug_openssl_path
193     for arg in args:
194         if arg.lower() == "verbose":
195             verbose = True
196             continue
197         if arg.lower() == "keep":
198             enable_keep = True
199             continue
200         if arg.lower() == "help":
201             want_help = True
202             continue
203         di = re.match(r'disable-(.*)', arg, re.I)
204         if di:
205             appargs(arg)
206             myenable(di.group(1), False)
207             continue
208         en = re.match(r'enable-(.*)', arg, re.I)
209         if en:
210             appargs(arg)
211             myenable(en.group(1), True)
212             continue
213         wo = re.match(r'without-(.*)', arg, re.I)
214         if wo:
215             appargs(arg)
216             mywith(wo.group(1), False)
217             continue
218         wv = re.match(r'with-(.*)=(.*)', arg, re.I)
219         if wv:
220             appargs(arg)
221             if wv.group(2).lower() == "no":
222                 mywith(wv.group(1), False)
223                 continue
224             mywith(wv.group(1), True, wv.group(2))
225             continue
226         wi = re.match(r'with-(.*)', arg, re.I)
227         if wi:
228             appargs(arg)
229             mywith(wi.group(1), True)
230             continue
231         if arg.lower() == "clean":
232             want_clean = True
233             continue
234         want_unknown = True
235         unknown_value = arg
236         break
237
238     # debug
239     if enable_debug:
240         if debug_botan_path is None:
241             debug_botan_path = botan_path + "_d"
242         if debug_openssl_path is None:
243             debug_openssl_path = openssl_path + "_d"
244
245 def appargs(arg):
246     """append seen arguments to configargs"""
247     global configargs
248     # escape backslashes, spaces and double quotes
249     escaped = ""
250
251     for x in arg:
252         if (x == "\\") or (x == " ") or (x == "\""):
253             escaped += "\\"
254         escaped += x
255     if configargs:
256         configargs += " " + escaped
257     else:
258         configargs = escaped
259
260 def myenable(key, val):
261     """parse enable/disable"""
262     global platform
263     global enable_debug
264     global enable_ecc
265     global enable_gost
266     global enable_static_runtime
267     global enable_non_paged
268     global enable_keep
269     global verbose
270     global want_unknown
271     global unknown_value
272     if key.lower() == "64bit":
273         if val:
274             platform = 64
275         return
276     if key.lower() == "debug":
277         if not val:
278             enable_debug = False
279         return
280     if key.lower() == "ecc":
281         if not val:
282             enable_ecc = False
283         return
284     if key.lower() == "gost":
285         if not val:
286             enable_gost = False
287         return
288     if key.lower() == "static-runtime":
289         if val:
290             enable_static_runtime = True
291         return
292     if key.lower() == "non-paged-memory":
293         if not val:
294             enable_non_paged = False
295         return
296     if key.lower() == "keep":
297         if val:
298             enable_keep = True
299         return
300     if key.lower() == "verbose":
301         if val:
302             verbose = True
303         return
304     want_unknown = True
305     if not val:
306         unknown_value = "disable-" + key
307     else:
308         unknown_value = "enable-" + key
309
310 def mywith(key, val, detail=None):
311     """parse with/without"""
312     global crypto_backend
313     global botan_path
314     global debug_botan_path
315     global openssl_path
316     global debug_openssl_path
317     global want_tests
318     global cppunit_path
319     global want_unknown
320     global unknown_value
321     global toolset
322
323     if key.lower() == "crypto-backend":
324         if val and (detail.lower() == "openssl"):
325             crypto_backend = "openssl"
326             return
327         if val and (detail.lower() == "botan"):
328             crypto_backend = "botan"
329             return
330         want_unknown = True
331         unknown_value = "with-crypto-backend=" + detail
332         return
333     if key.lower() == "botan":
334         if not val:
335             want_unknown = True
336             unknown_value = "without-botan doesn't make sense"
337             return
338         if detail.lower() != "yes":
339             botan_path = detail
340         return
341     if key.lower() == "debug-botan":
342         if not val:
343             want_unknown = True
344             unknown_value = "without-debug-botan doesn't make sense"
345             return
346         if detail.lower() != "yes":
347             debug_botan_path = detail
348         return
349     if key.lower() == "openssl":
350         if not val:
351             want_unknown = True
352             unknown_value = "without-openssl doesn't make sense"
353             return
354         if detail.lower() != "yes":
355             openssl_path = detail
356         return
357     if key.lower() == "debug-openssl":
358         if not val:
359             want_unknown = True
360             unknown_value = "without-debug-openssl doesn't make sense"
361             return
362         if detail.lower() != "yes":
363             debug_openssl_path = detail
364         return
365     if key.lower() == "cppunit":
366         if not val:
367             want_tests = False
368             return
369         if detail.lower() != "yes":
370             cppunit_path = detail
371         return
372     if key.lower() == "toolset":
373         if not val:
374             want_tests = False
375             return
376         if detail:
377             toolset=detail.lower()
378         return
379     want_unknown = True
380     if not val:
381         unknown_value = "without-" + key
382     else:
383         unknown_value = "with-" + key
384
385 def dohelp():
386     """help"""
387     for line in myhelp:
388         print(line)
389     sys.exit(1)
390
391 def docleantest():
392     """clean test files"""
393     for basename in testlist:
394         filename = "test" + basename + ".c"
395         if os.path.isfile(filename):
396             os.unlink(filename)
397         filename = "test" + basename + ".cpp"
398         if os.path.isfile(filename):
399             os.unlink(filename)
400         filename = "test" + basename + ".obj"
401         if os.path.isfile(filename):
402             os.unlink(filename)
403         filename = "test" + basename + ".exe"
404         if os.path.isfile(filename):
405             os.unlink(filename)
406     if os.path.isfile("botan.dll"):
407         os.unlink("botan.dll")
408     if os.path.isfile("libeay32.dll"):
409         os.unlink("libeay32.dll")
410     if os.path.isfile("libeaycompat32.dll"):
411         os.unlink("libeaycompat32.dll")
412     if os.path.isfile("libcrypto-1_1.dll"):
413         os.unlink("libcrypto-1_1.dll")
414     if os.path.isfile("libcrypto-1_1-x64.dll"):
415         os.unlink("libcrypto-1_1-x64.dll")
416
417 def doclean():
418     """clean"""
419     docleantest()
420     for filename in filelist:
421         if os.path.isfile(filename):
422             os.unlink(filename)
423     sys.exit(0)
424
425 def dounknown():
426     """parsing error"""
427     print("can't parse " + unknown_value + "", file=sys.stderr)
428     sys.exit(1)
429
430 def doconfig():
431     """config itself"""
432     global botan_path
433     global debug_botan_path
434     global openssl_path
435     global debug_openssl_path
436     global cppunit_path
437
438     # configure the platform
439     if platform == 32:
440         varvals["PLATFORM"] = "Win32"
441     else:
442         varvals["PLATFORM"] = "x64"
443         varvals["PLATFORMDIR"] = "x64\\"
444
445     # configure the runtime library
446     if enable_static_runtime:
447         varvals["RUNTIMELIBRARY"] = "MultiThreaded"
448     else:
449         varvals["RUNTIMELIBRARY"] = "MultiThreadedDLL"
450
451     # configure ECC and GOST
452     if enable_ecc:
453         condvals["ECC"] = True
454     if enable_gost:
455         condvals["GOST"] = True
456
457     # configure the crypto
458     if crypto_backend == "botan":
459         condvals["BOTAN"] = True
460         varvals["LIBNAME"] = "botan.lib"
461         botan_path = os.path.abspath(botan_path)
462         botan_inc = os.path.join(botan_path, "include")
463         botan_dll = ""
464         if os.path.exists(os.path.join(botan_inc, "botan-2\\botan\\init.h")):
465             varvals["INCLUDEPATH"] = os.path.join(botan_inc, "botan-2")
466         elif os.path.exists(os.path.join(botan_inc, "botan-1.11\\botan\\init.h")):
467             varvals["INCLUDEPATH"] = os.path.join(botan_inc, "botan-1.11")
468         elif os.path.exists(os.path.join(botan_inc, "botan\\init.h")):
469             varvals["INCLUDEPATH"] = botan_inc
470         else:
471             print("can't find Botan includes", file=sys.stderr)
472             sys.exit(1)
473         if os.path.exists(os.path.join(botan_path, "lib\\botan.lib")):
474             varvals["LIBPATH"] = os.path.join(botan_path, "lib")
475             botan_dll = os.path.join(botan_path, "lib\\botan.dll")
476         elif os.path.exists(os.path.join(botan_path, "botan.lib")):
477             varvals["LIBPATH"] = botan_path
478             botan_dll = os.path.join(botan_path, "botan.dll")
479         else:
480             print("can't find Botan library", file=sys.stderr)
481             sys.exit(1)
482         varvals["DLLPATH"] = botan_dll
483         if enable_debug:
484             debug_botan_path = os.path.abspath(debug_botan_path)
485             debug_botan_inc = os.path.join(debug_botan_path, "include")
486             debug_botan_dll = ""
487             if os.path.exists(os.path.join(debug_botan_inc, "botan-2\\botan\\init.h")):
488                 varvals["DEBUGINCPATH"] = os.path.join(debug_botan_inc, "botan-2")
489             elif os.path.exists(os.path.join(debug_botan_inc, "botan-1.11\\botan\\init.h")):
490                 varvals["DEBUGINCPATH"] = os.path.join(debug_botan_inc, "botan-1.11")
491             elif os.path.exists(os.path.join(debug_botan_inc, "botan\\init.h")):
492                 varvals["DEBUGINCPATH"] = debug_botan_inc
493             else:
494                 print("can't find debug Botan includes", file=sys.stderr)
495                 sys.exit(1)
496             if os.path.exists(os.path.join(debug_botan_path, "lib\\botan.lib")):
497                 varvals["DEBUGLIBPATH"] = os.path.join(debug_botan_path, "lib")
498                 debug_botan_dll = os.path.join(debug_botan_path, "lib\\botan.dll")
499             if os.path.exists(os.path.join(debug_botan_path, "botan.lib")):
500                 varvals["DEBUGLIBPATH"] = debug_botan_path
501                 debug_botan_dll = os.path.join(debug_botan_path, "botan.dll")
502             else:
503                 print("can't find debug Botan library", file=sys.stderr)
504                 sys.exit(1)
505             varvals["DEBUGDLLPATH"] = debug_botan_dll
506         else:
507             varvals["DEBUGDLLPATH"] = varvals["DLLPATH"]
508             varvals["DEBUGINCPATH"] = varvals["INCLUDEPATH"]
509             varvals["DEBUGLIBPATH"] = varvals["LIBPATH"]
510
511         # Botan version
512         if verbose:
513             print("checking Botan version")
514         botan_version_major = 0
515         botan_version_minor = 0
516         system_libs = []
517         if os.path.exists(botan_dll):
518             subprocess.call(["copy", botan_dll, "."], shell=True)
519         else:
520             system_libs = ["user32.lib", "advapi32.lib"]
521         inc = varvals["INCLUDEPATH"]
522         lib = os.path.join(varvals["LIBPATH"], "botan.lib")
523         testfile = open("testbotan.cpp", "w")
524         print('\
525 #include <botan/version.h>\n\
526 int main() {\n\
527 #if BOTAN_VERSION_CODE >= BOTAN_VERSION_CODE_FOR(2,0,0)\n\
528  return 3;\n\
529 #elif BOTAN_VERSION_CODE >= BOTAN_VERSION_CODE_FOR(1,11,0)\n\
530  return 2;\n\
531 #elif BOTAN_VERSION_CODE >= BOTAN_VERSION_CODE_FOR(1,10,0)\n\
532  return 1;\n\
533 #else\n\
534  return 0;\n\
535 #endif\n\
536 }', file=testfile)
537         testfile.close()
538         command = ["cl", "/nologo", "/MD", "/I", inc, "testbotan.cpp", lib]
539         command.extend(system_libs)
540         subprocess.check_output(command, stderr=subprocess.STDOUT)
541         if not os.path.exists(".\\testbotan.exe"):
542             print("can't create .\\testbotan.exe", file=sys.stderr)
543             sys.exit(1)
544         ret = subprocess.call(".\\testbotan.exe")
545         if ret == 0:
546             print("Botan version too old", file=sys.stderr)
547             sys.exit(1)
548         elif ret == 1:
549             botan_version_major = 1
550             botan_version_minor = 10
551         elif ret == 2:
552             botan_version_major = 1
553             botan_version_minor = 11
554             print("Botan version 1.11 not yet supported", file=sys.stderr)
555             sys.exit(1)
556         elif ret == 3:
557             botan_version_major = 2
558             botan_version_minor = 0
559             print("Botan version 2.0 not yet supported", file=sys.stderr)
560             sys.exit(1)
561         else:
562             print("Botan test failed", file=sys.stderr)
563             sys.exit(1)
564
565         # Botan ECC support
566         if enable_ecc:
567             if verbose:
568                 print("checking Botan ECC support")
569             testfile = open("testecc.cpp", "w")
570             print('\
571 #include <botan/init.h>\n\
572 #include <botan/ec_group.h>\n\
573 #include <botan/oids.h>\n\
574 #include <botan/version.h>\n\
575 int main() {\n\
576  Botan::LibraryInitializer::initialize();\n\
577  const std::string name("secp256r1");\n\
578  const Botan::OID oid(Botan::OIDS::lookup(name));\n\
579  const Botan::EC_Group ecg(oid);\n\
580  try {\n\
581 #if BOTAN_VERSION_CODE >= BOTAN_VERSION_CODE_FOR(1,11,0)\n\
582   const std::vector<Botan::byte> der =\n\
583    ecg.DER_encode(Botan::EC_DOMPAR_ENC_OID);\n\
584 #else\n\
585   const Botan::SecureVector<Botan::byte> der =\n\
586    ecg.DER_encode(Botan::EC_DOMPAR_ENC_OID);\n\
587 #endif\n\
588  } catch(...) {\n\
589   return 1;\n\
590  }\n\
591  return 0;\n\
592 }', file=testfile)
593             testfile.close()
594             command = ["cl", "/nologo", "/MD", "/I", inc, "testecc.cpp", lib]
595             command.extend(system_libs)
596             subprocess.check_output(command, stderr=subprocess.STDOUT)
597             if not os.path.exists(".\\testecc.exe"):
598                 print("can't create .\\testecc.exe", file=sys.stderr)
599                 sys.exit(1)
600             if subprocess.call(".\\testecc.exe") != 0:
601                 print("can't find P256: upgrade to Botan >= 1.10.6", file=sys.stderr)
602                 sys.exit(1)
603
604         # Botan GOST support
605         if enable_gost:
606             if verbose:
607                 print("checking Botan GOST support")
608             testfile = open("testgost.cpp", "w")
609             print('\
610 #include <botan/init.h>\n\
611 #include <botan/gost_3410.h>\n\
612 #include <botan/oids.h>\n\
613 #include <botan/version.h>\n\
614 int main() {\n\
615  Botan::LibraryInitializer::initialize();\n\
616  const std::string name("gost_256A");\n\
617  const Botan::OID oid(Botan::OIDS::lookup(name));\n\
618  const Botan::EC_Group ecg(oid);\n\
619  try {\n\
620 #if BOTAN_VERSION_CODE >= BOTAN_VERSION_CODE_FOR(1,11,0)\n\
621   const std::vector<Botan::byte> der =\n\
622    ecg.DER_encode(Botan::EC_DOMPAR_ENC_OID);\n\
623 #else\n\
624   const Botan::SecureVector<Botan::byte> der =\n\
625    ecg.DER_encode(Botan::EC_DOMPAR_ENC_OID);\n\
626 #endif\n\
627  } catch(...) {\n\
628   return 1;\n\
629  }\n\
630  return 0;\n\
631 }', file=testfile)
632             testfile.close()
633             command = ["cl", "/nologo", "/MD", "/I", inc, "testgost.cpp", lib]
634             command.extend(system_libs)
635             subprocess.check_output(command, stderr=subprocess.STDOUT)
636             if not os.path.exists(".\\testgost.exe"):
637                 print("can't create .\\testgost.exe", file=sys.stderr)
638                 sys.exit(1)
639             if subprocess.call(".\\testgost.exe") != 0:
640                 print("can't find GOST: upgrade to Botan >= 1.10.6", file=sys.stderr)
641                 sys.exit(1)
642
643         # no check for Botan RFC3394 support
644         condvals["RFC3394"] = True
645
646         # Botan RFC5649 support
647         if verbose:
648             print("checking Botan RFC5649 support")
649         testfile = open("testrfc5649.cpp", "w")
650         print('\
651 #include <botan/botan.h>\n\
652 #include <botan/rfc3394.h>\n\
653 #include <botan/version.h>\n\
654 using namespace Botan;\n\
655 int main() {\n\
656 #if BOTAN_VERSION_CODE >= BOTAN_VERSION_CODE_FOR(1,11,0)\n\
657  secure_vector<byte> key(10);\n\
658  SymmetricKey kek("AABB");\n\
659  secure_vector<byte> x = rfc5649_keywrap(key, kek);\n\
660 #else\n\
661  SecureVector<byte> key(10);\n\
662  SymmetricKey kek("AABB");\n\
663  Algorithm_Factory& af = global_state().algorithm_factory();\n\
664  SecureVector<byte> x = rfc5649_keywrap(key, kek, af);\n\
665 #endif\n\
666  return 1;\n\
667 }', file=testfile)
668         testfile.close()
669         command = ["cl", "/nologo", "/MD", "/EHsc","/I", inc, "testrfc5649.cpp", lib]
670         command.extend(system_libs)
671         subprocess.call(command)
672         if os.path.exists(".\\testrfc5649.exe"):
673             if verbose:
674                 print("Found AES key wrap with pad")
675             condvals["RFC5649"] = True
676         else:
677             if verbose:
678                 print("can't compile Botan AES key wrap with pad")
679
680         # Botan GNU MP support
681         if botan_version_major == 1 and botan_version_minor == 10:
682             if verbose:
683                 print("checking Botan GNU MP support")
684             testfile = open("testgnump.cpp", "w")
685             print('\
686 #include <botan/build.h>\n\
687 int main() {\n\
688 #ifndef BOTAN_HAS_ENGINE_GNU_MP\n\
689 #error "No GNU MP support";\n\
690 #endif\n\
691 }', file=testfile)
692             testfile.close()
693             command = ["cl", "/nologo", "/MD", "/I", inc, "testgnump.cpp", lib]
694             command.extend(system_libs)
695             subprocess.call(command)
696             if os.path.exists(".\\testgnump.exe"):
697                 if verbose:
698                     print("Botan GNU MP is supported")
699             else:
700                 if verbose:
701                     print("Botan GNU MP is not supported")
702
703         # Botan raw PSS support
704         if verbose:
705             print("checking Botan raw PSS support")
706         testfile = open("testrawpss.cpp", "w")
707         print('\
708 #include <botan/botan.h>\n\
709 #include <botan/version.h>\n\
710 int main() {\n\
711 #if BOTAN_VERSION_CODE >= BOTAN_VERSION_CODE_FOR(2,3,0)\n\
712  return 0;\n\
713 #endif\n\
714  return 1;\n\
715 }', file=testfile)
716         testfile.close()
717         command = ["cl", "/nologo", "/MD", "/I", inc, "testrawpss.cpp", lib]
718         command.extend(system_libs)
719         subprocess.check_output(command, stderr=subprocess.STDOUT)
720         if not os.path.exists(".\\testrawpss.exe"):
721             if verbose:
722                 print("can't create .\\testrawpss.exe", file=sys.stderr)
723         else:
724             if subprocess.call(".\\testrawpss.exe") != 0:
725                 if verbose:
726                     print("can't find raw PSS: upgrade to Botan >= 2.3.0", file=sys.stderr)
727             else:
728                 condvals["RAWPSS"] = True
729
730     else:
731
732         condvals["OPENSSL"] = True
733         varvals["EXTRALIBS"] = "crypt32.lib;ws2_32.lib;"
734         openssl_path = os.path.abspath(openssl_path)
735         openssl_inc = os.path.join(openssl_path, "include")
736         if not os.path.exists(os.path.join(openssl_inc, "openssl\\ssl.h")):
737             print("can't find OpenSSL headers", file=sys.stderr)
738             sys.exit(1)
739         varvals["INCLUDEPATH"] = openssl_inc
740         openssl_lib = os.path.join(openssl_path, "lib")
741         openssl_lib_name = ""
742         openssl_lib_dll = ""
743         if os.path.exists(os.path.join(openssl_lib, "libeay32.lib")):
744             openssl_lib_name = "libeay32.lib"
745             openssl_lib_dll = "bin\\libeay32.dll"
746         elif os.path.exists(os.path.join(openssl_lib, "libeaycompat32.lib")):
747             openssl_lib_name = "libeaycompat32.lib"
748             openssl_lib_dll = "bin\\libeaycompat32.dll"
749         elif os.path.exists(os.path.join(openssl_lib, "libcrypto.lib")):
750             openssl_lib_name = "libcrypto.lib"
751             if platform == 32:
752                 openssl_lib_dll = "bin\\libcrypto-1_1.dll"
753             else:
754                 openssl_lib_dll = "bin\\libcrypto-1_1-x64.dll"
755
756         else:
757             print("can't find OpenSSL library", file=sys.stderr)
758             sys.exit(1)
759         openssl_dll = os.path.join(openssl_path,openssl_lib_dll)
760         varvals["LIBPATH"] = openssl_lib
761         varvals["LIBNAME"] = openssl_lib_name
762         varvals["DLLPATH"] = openssl_dll
763         if enable_debug:
764             debug_openssl_path = os.path.abspath(debug_openssl_path)
765             varvals["DEBUGDLLPATH"] = \
766                 os.path.join(debug_openssl_path, openssl_lib_dll)
767             debug_openssl_inc = os.path.join(debug_openssl_path, "include")
768             if not os.path.exists(os.path.join(debug_openssl_inc,
769                                                "openssl\\ssl.h")):
770                 print("can't find debug OpenSSL headers", file=sys.stderr)
771                 sys.exit(1)
772             varvals["DEBUGINCPATH"] = debug_openssl_inc
773             debug_openssl_lib = os.path.join(debug_openssl_path, "lib")
774             if not os.path.exists(os.path.join(debug_openssl_lib,
775                                                openssl_lib_name)):
776                 print("can't find debug OpenSSL library", file=sys.stderr)
777                 sys.exit(1)
778             varvals["DEBUGLIBPATH"] = debug_openssl_lib
779         else:
780             varvals["DEBUGDLLPATH"] = varvals["DLLPATH"]
781             varvals["DEBUGINCPATH"] = varvals["INCLUDEPATH"]
782             varvals["DEBUGLIBPATH"] = varvals["LIBPATH"]
783
784         # OpenSSL support
785         if verbose:
786             print("checking OpenSSL")
787         system_libs = []
788         if os.path.exists(openssl_dll):
789             subprocess.call(["copy", openssl_dll, "."], shell=True)
790         else:
791             system_libs = ["user32.lib", "advapi32.lib", "gdi32.lib", "crypt32.lib", "ws2_32.lib"]
792         inc = openssl_inc
793         lib = os.path.join(openssl_lib, openssl_lib_name)
794         testfile = open("testossl.c", "w")
795         print('\
796 #include <openssl/err.h>\n\
797 int main() {\n\
798  ERR_clear_error();\n\
799  return 0;\n\
800 }', file=testfile)
801         testfile.close()
802         command = ["cl", "/nologo", "/MD", "/I", inc, "testossl.c", lib]
803         command.extend(system_libs)
804         subprocess.check_output(command, stderr=subprocess.STDOUT)
805         if not os.path.exists(".\\testossl.exe"):
806             print("can't create .\\testossl.exe", file=sys.stderr)
807             sys.exit(1)
808         if subprocess.call(".\\testossl.exe") != 0:
809             print("OpenSSL test failed", file=sys.stderr)
810             sys.exit(1)
811
812         # OpenSSL version
813         if verbose:
814             print("checking OpenSSL version")
815         testfile = open("testosslv.c", "w")
816         print('\
817 #include <openssl/ssl.h>\n\
818 #include <openssl/opensslv.h>\n\
819 int main() {\n\
820 #ifndef OPENSSL_VERSION_NUMBER\n\
821  return -1;\n\
822 #endif\n\
823 #if OPENSSL_VERSION_NUMBER >= 0x010000000L\n\
824  return 0;\n\
825 #else\n\
826  return 1;\n\
827 #endif\n\
828 }', file=testfile)
829         testfile.close()
830         command = ["cl", "/nologo", "/MD", "/I", inc, "testosslv.c", lib]
831         command.extend(system_libs)
832         subprocess.check_output(command, stderr=subprocess.STDOUT)
833         if not os.path.exists(".\\testosslv.exe"):
834             print("can't create .\\testosslv.exe", file=sys.stderr)
835             sys.exit(1)
836         if subprocess.call(".\\testosslv.exe") != 0:
837             print("OpenSLL version too old (1.0.0 or later required)", file=sys.stderr)
838             sys.exit(1)
839
840         # OpenSSL ECC support
841         if enable_ecc:
842             if verbose:
843                 print("checking OpenSSL ECC support")
844             testfile = open("testecc.c", "w")
845             print('\
846 #include <openssl/ecdsa.h>\n\
847 #include <openssl/objects.h>\n\
848 int main() {\n\
849  EC_KEY *ec256, *ec384, *ec521;\n\
850  ec256 = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1);\n\
851  ec384 = EC_KEY_new_by_curve_name(NID_secp384r1);\n\
852  ec521 = EC_KEY_new_by_curve_name(NID_secp521r1);\n\
853  if (ec256 == NULL || ec384 == NULL || ec521 == NULL)\n\
854   return 1;\n\
855  return 0;\n\
856 }', file=testfile)
857             testfile.close()
858             command = ["cl", "/nologo", "/MD", "/I", inc, "testecc.c", lib]
859             command.extend(system_libs)
860             subprocess.check_output(command, stderr=subprocess.STDOUT)
861             if not os.path.exists(".\\testecc.exe"):
862                 print("can't create .\\testecc.exe", file=sys.stderr)
863                 sys.exit(1)
864             if subprocess.call(".\\testecc.exe") != 0:
865                 print("can't find P256, P384, or P521: no ECC support", file=sys.stderr)
866                 sys.exit(1)
867
868         # OpenSSL GOST support
869         if enable_gost:
870             if verbose:
871                 print("checking OpenSSL GOST support")
872             testfile = open("testgost.c", "w")
873             print('\
874 #include <openssl/conf.h>\n\
875 #include <openssl/engine.h>\n\
876 #include <openssl/crypto.h>\n\
877 #include <openssl/opensslv.h>\n\
878 int main() {\n\
879  ENGINE *eg;\n\
880  const EVP_MD* EVP_GOST_34_11;\n\
881  OpenSSL_add_all_algorithms();\n\
882 #if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER)\n\
883  ENGINE_load_builtin_engines();\n\
884 #else\n\
885  OPENSSL_init_crypto(OPENSSL_INIT_ENGINE_ALL_BUILTIN | OPENSSL_INIT_LOAD_CONFIG, NULL);\n\
886 #endif\n\
887  eg = ENGINE_by_id("gost");\n\
888  if (eg == NULL)\n\
889   return 1;\n\
890  if (ENGINE_init(eg) <= 0)\n\
891   return 1;\n\
892  EVP_GOST_34_11 = ENGINE_get_digest(eg, NID_id_GostR3411_94);\n\
893  if (EVP_GOST_34_11 == NULL)\n\
894   return 1;\n\
895  if (ENGINE_register_pkey_asn1_meths(eg) <= 0)\n\
896   return 1;\n\
897  if (ENGINE_ctrl_cmd_string(eg, "CRYPT_PARAMS",\n\
898      "id-Gost28147-89-CryptoPro-A-ParamSet", 0) <= 0)\n\
899   return 1;\n\
900  return 0;\n\
901 }', file=testfile)
902             testfile.close()
903             command = ["cl", "/nologo", "/MD", "/I", inc, "testgost.c", lib]
904             command.extend(system_libs)
905             subprocess.check_output(command, stderr=subprocess.STDOUT)
906             if not os.path.exists(".\\testgost.exe"):
907                 print("can't create .\\testgost.exe", file=sys.stderr)
908                 sys.exit(1)
909             if subprocess.call(".\\testgost.exe") != 0:
910                 print("can't find GOST: no GOST support", file=sys.stderr)
911                 sys.exit(1)
912
913         # OpenSSL EVP interface for AES key wrapping (aka RFC 3394)
914         if verbose:
915             print("checking OpenSSL EVP interface for AES key wrapping")
916         testfile = open("testrfc3394.c", "w")
917         print('\
918 #include <openssl/evp.h>\n\
919 int main() {\n\
920  EVP_aes_128_wrap();\n\
921  return 1;\n\
922 }', file=testfile)
923         testfile.close()
924         command = ["cl", "/nologo", "/MD", "/I", inc, "testrfc3394.c", lib]
925         command.extend(system_libs)
926         subprocess.call(command)
927         if os.path.exists(".\\testrfc3394.exe"):
928             if verbose:
929                 print("RFC 3394 is supported")
930             condvals["RFC3394"] = True
931         else:
932             if verbose:
933                 print("can't compile OpenSSL RFC 3394")
934
935         # OpenSSL EVP interface for AES key wrap with pad (aka RFC 5649)
936         if verbose:
937             print("checking OpenSSL EVP interface for AES key wrapping with pad")
938         testfile = open("testrfc5649.c", "w")
939         print('\
940 #include <openssl/evp.h>\n\
941 int main() {\n\
942  EVP_aes_128_wrap_pad();\n\
943  return 1;\n\
944 }', file=testfile)
945         testfile.close()
946         command = ["cl", "/nologo", "/MD", "/I", inc, "testrfc5649.c", lib]
947         command.extend(system_libs)
948         subprocess.call(command)
949         if os.path.exists(".\\testrfc5649.exe"):
950             if verbose:
951                 print("RFC 5649 is supported")
952             condvals["RFC5649"] = True
953         else:
954             if verbose:
955                 print("can't compile OpenSSL RFC 5649")
956
957         # no check for OpenSSL raw PSS support
958         condvals["RAWPSS"] = True
959         # no check for OpenSSL AES GCM
960         condvals["AESGCM"] = True
961
962     # configure CppUnit
963     if want_tests:
964         condvals["TESTS"] = True
965         cppunit_path = os.path.abspath(cppunit_path)
966         cppunit_inc = os.path.join(cppunit_path, "include")
967         if not os.path.exists(os.path.join(cppunit_inc, "cppunit\\Test.h")):
968             print("can't find CppUnit headers", file=sys.stderr)
969             sys.exit(1)
970         varvals["CUINCPATH"] = cppunit_inc
971         cppunit_lib = os.path.join(cppunit_path, "lib")
972         if not os.path.exists(os.path.join(cppunit_lib, "cppunit.lib")):
973             cppunit_lib = cppunit_path
974         if not os.path.exists(os.path.join(cppunit_lib, "cppunit.lib")):
975             print("can't find CppUnit library", file=sys.stderr)
976             sys.exit(1)
977         if enable_debug:
978             if not os.path.exists(os.path.join(cppunit_lib, "cppunitd.lib")):
979                 print("can't find debug CppUnit library", file=sys.stderr)
980                 sys.exit(1)
981         varvals["CULIBPATH"] = cppunit_lib
982
983     # misc
984     if enable_non_paged:
985         condvals["NONPAGE"] = True
986
987 def kw(path):
988     """escape spaces"""
989     if re.search(r' ', path):
990         return '"' + path + '"'
991     else:
992         return path
993
994 def setupfile(filename):
995     """setup files with condition stacks and variable expansions"""
996     cond = "@@@"
997     conds = []
998     passing = True
999     passes = []
1000     filein = open(filename + ".in", "r")
1001     fileout = open(filename, "w")
1002
1003     for line in filein:
1004         line = line.rstrip("\r\n")
1005         cif = re.match(r'@IF (.*)', line)
1006         if cif:
1007             conds.append(cond)
1008             passes.append(passing)
1009             cond = cif.group(1)
1010             if condvals.get(cond):
1011                 # do nothing
1012                 pass
1013             else:
1014                 passing = False
1015             continue
1016         celse = re.match(r'@ELSE (.*)', line)
1017         if celse:
1018             if cond != celse.group(1):
1019                 raise SyntaxError("@ELSE " + celse.group(1) +
1020                                   " mismatch in " + filename)
1021             if condvals.get(cond):
1022                 passing = False
1023             else:
1024                 if len(passes) > 0:
1025                     passing = passes[-1]
1026                 else:
1027                     passing = True
1028             continue
1029         cend = re.match(r'@END (.*)', line)
1030         if cend:
1031             if cond != cend.group(1):
1032                 raise SyntaxError("@END " + cend.group(1) +
1033                                   " mismatch in " + filename)
1034             cond = conds.pop()
1035             if len(passes) > 0:
1036                 passing = passes.pop()
1037             else:
1038                 passing = True
1039             continue
1040         if not passing:
1041             continue
1042         while True:
1043             vm = re.match(r'([^@]*)@([^@ ]*)@(.*)', line)
1044             if vm:
1045                 if vm.group(2) in varnames:
1046                     if varvals.get(vm.group(2)):
1047                         val = kw(varvals[vm.group(2)])
1048                     else:
1049                         val = ""
1050                     line = vm.group(1) + val + vm.group(3)
1051                     continue
1052                 else:
1053                     raise SyntaxError("unknown control @" + vm.group(2) +
1054                                       "@ in " + filename)
1055             break
1056         print(line, file=fileout)
1057     if verbose:
1058         print("Setting up " + filename)
1059     filein.close()
1060     fileout.close()
1061
1062 def main(args):
1063     """run it"""
1064
1065     # no arguments -> usage
1066     if len(args) <= 1:
1067         for line in usage:
1068             print(line)
1069         sys.exit(1)
1070
1071     parseargs(args[1:])
1072
1073     if want_help:
1074         dohelp()
1075     if want_clean:
1076         doclean()
1077     if want_unknown:
1078         dounknown()
1079     if not toolset:
1080         dodetectvisualstudio()
1081     if not toolset:
1082         print("Build skipped. To build, this file needs to run from VS command prompt.")
1083         sys.exit(1)
1084
1085     varvals["PLATFORMTOOLSET"] = toolset
1086
1087     # status before config
1088     if verbose:
1089         if enable_keep:
1090             print("keep: enabled")
1091         else:
1092             print("keep: disabled")
1093         if platform == 64:
1094             print("64bit: enabled")
1095         else:
1096             print("64bit: disabled")
1097         if enable_debug:
1098             print("debug: enabled")
1099         else:
1100             print("debug: disabled")
1101         if enable_ecc:
1102             print("ecc: enabled")
1103         else:
1104             print("ecc: disabled")
1105         if enable_gost:
1106             print("gost: enabled")
1107         else:
1108             print("gost: disabled")
1109         if enable_non_paged:
1110             print("non-paged-memory: enabled")
1111         else:
1112             print("non-paged-memory: disabled")
1113         print("crypto-backend: " + crypto_backend)
1114         if crypto_backend == "botan":
1115             print("botan-path: " + botan_path)
1116             if enable_debug:
1117                 print("debug-botan-path: " + debug_botan_path)
1118         else:
1119             print("openssl-path: " + openssl_path)
1120             if enable_debug:
1121                 print("debug-openssl-path: " + debug_openssl_path)
1122         if want_tests:
1123             print("cppunit-path: " + cppunit_path)
1124         print("toolset: "+toolset)
1125
1126
1127     doconfig()
1128
1129     # status after config
1130     if verbose:
1131         print("Configuration Status")
1132         print("\tconditions:")
1133         for name in condnames:
1134             if condvals.get(name):
1135                 print("\t\t" + name + " is true")
1136             else:
1137                 print("\t\t" + name + " is false")
1138         print("\tsubstitutions:")
1139         for name in varnames:
1140             if varvals.get(name):
1141                 print("\t\t" + name + '-> "' + varvals[name] + '"')
1142         print()
1143
1144     for filename in filelist:
1145         setupfile(filename)
1146
1147     # clean test file
1148     if not enable_keep:
1149         docleantest()
1150
1151     print("Configured.")
1152     sys.exit(0)
1153
1154 main(sys.argv)
1155
1156 # Notes: Unix configure.ac options
1157 #  --enable-64bit supported
1158 #  --enable-ecc supported
1159 #  --enable-gost supported
1160 #  --enable-non-paged-memory supported
1161 #  --enable-visibility (enforced by DLLs)
1162 #  --with-crypto-backend supported
1163 #  --with-botan supported (Release and Debug)
1164 #  --with-openssl supported (Release and Debug)
1165 #  --with-migrate (useless as SoftHSMv1 is not supported)
1166 #  --with-objectstore-backend-db (TODO)
1167 #  --with-sqlite3 (useless until objectstore backend can be chosen)