diff options
author | lloyd <[email protected]> | 2009-10-13 16:01:57 +0000 |
---|---|---|
committer | lloyd <[email protected]> | 2009-10-13 16:01:57 +0000 |
commit | 9268a0455a07d31a66364aa5b7594bd75250b466 (patch) | |
tree | 63b683ca95448ce083981d002d870a569c2c98a1 /doc | |
parent | 3bc2bb0461b1b40466821daf0061eab769621eab (diff) | |
parent | 5318b944acc2a5fa6d445784c710f37c793ff90b (diff) |
propagate from branch 'net.randombit.botan.1_8' (head c5ae189464f6ef16e3ce73ea7c563412460d76a3)
to branch 'net.randombit.botan' (head e2b95b6ad31c7539cf9ac0ebddb1d80bf63b5b21)
Diffstat (limited to 'doc')
-rw-r--r-- | doc/building.tex | 149 | ||||
-rw-r--r-- | doc/examples/bench.cpp | 57 | ||||
-rw-r--r-- | doc/examples/cpuid.cpp | 15 | ||||
-rw-r--r-- | doc/examples/package.cpp | 61 | ||||
-rw-r--r-- | doc/examples/tss.cpp | 38 | ||||
-rw-r--r-- | doc/log.txt | 20 | ||||
-rw-r--r-- | doc/python.tex | 68 | ||||
-rwxr-xr-x | doc/python/cipher.py | 44 | ||||
-rwxr-xr-x | doc/python/cryptobox.py | 34 | ||||
-rwxr-xr-x | doc/python/nisttest.py | 61 | ||||
-rw-r--r-- | doc/python/results.txt | 60 | ||||
-rwxr-xr-x | doc/python/rng_test.py | 22 | ||||
-rwxr-xr-x | doc/python/rsa.py | 31 | ||||
-rwxr-xr-x | doc/scripts/configure.pl | 2340 |
14 files changed, 2921 insertions, 79 deletions
diff --git a/doc/building.tex b/doc/building.tex index aa4435c14..f58786e76 100644 --- a/doc/building.tex +++ b/doc/building.tex @@ -13,7 +13,7 @@ \title{\textbf{Botan Build Guide}} \author{Jack Lloyd \\ \texttt{[email protected]}} -\date{2008-11-24} +\date{2009-10-09} \newcommand{\filename}[1]{\texttt{#1}} \newcommand{\module}[1]{\texttt{#1}} @@ -41,61 +41,58 @@ POSIX (such as VMS, MacOS 9, OS/390, OS/400, ...) are not supported by the build system, primarily due to lack of access. Please contact the maintainer if you would like to build Botan on such a system. -Botan's build is controlled by configure.pl, which is a Perl -script. Perl 5.6 or later is required. +Botan's build is controlled by configure.py, which is a Python +script. Python 2.4 or later is required. \section{For the Impatient} \begin{verbatim} -$ ./configure.pl [--prefix=/some/directory] +$ ./configure.py [--prefix=/some/directory] $ make $ make install \end{verbatim} Or using \verb|nmake|, if you're compiling on Windows with Visual C++. On platforms that do not understand the '\#!' convention for -beginning script files, or that have Perl installed in an unusual -spot, you might need to prefix the \texttt{configure.pl} command with -\texttt{perl} or \texttt{/path/to/perl}. +beginning script files, or that have Python installed in an unusual +spot, you might need to prefix the \texttt{configure.py} command with +\texttt{python} or \texttt{/path/to/python}. \section{Building the Library} -The first step is to run \filename{configure.pl}, which is a Perl +The first step is to run \filename{configure.py}, which is a Python script that creates various directories, config files, and a Makefile -for building everything. The script requires at least Perl 5.6; any -later version should also work. +for building everything. The script requires at least Python 2.4; any +later version of Python 2.x should also work. The script will attempt to guess what kind of system you are trying to compile for (and will print messages telling you what it guessed). You can override this process by passing the options \verb|--cc|, -\verb|--os|, and \verb|--cpu| -- acceptable values are printed if -you run \verb|configure.pl| with \verb|--help|. +\verb|--os|, and \verb|--cpu|. You can pass basically anything reasonable with \verb|--cpu|: the script knows about a large number of different architectures, their -sub-models, and common aliases for them. The script does not display -all the possibilities in its help message because there are simply too -many entries. You should only select the 64-bit version of a CPU (such -as ``sparc64'' or ``mips64'') if your operating system knows how to -handle 64-bit object code -- a 32-bit kernel on a 64-bit CPU will -generally not like 64-bit code. +sub-models, and common aliases for them. You should only select the +64-bit version of a CPU (such as ``sparc64'' or ``mips64'') if your +operating system knows how to handle 64-bit object code -- a 32-bit +kernel on a 64-bit CPU will generally not like 64-bit code. By default the script tries to figure out what will work on your -system, and use that. It will print a display at the end showing -which algorithms have and have not been abled. For instance on one -system we might see the line: +system, and use that. It will print a display at the end showing which +algorithms have and have not been enabled. For instance on one system +we might see lines like: \begin{verbatim} - (loading): entropy: [beos_stats] buf_es [cryptoapi_rng] - dev_random egd proc_walk unix_procs [win32_stats] + INFO: Skipping mod because CPU incompatible - asm_amd64 mp_amd64 mp_asm64 sha1_amd64 + INFO: Skipping mod because OS incompatible - cryptoapi_rng win32_stats + INFO: Skipping mod because compiler incompatible - mp_ia32_msvc + INFO: Skipping mod because loaded on request only - bzip2 gnump openssl qt_mutex zlib \end{verbatim} -The names listed in brackets are disabled, the others are -enabled. Here we see the list of entropy sources which are going to be -compiled into Botan. Since this particular line comes when Botan was -configuring for a Linux system, the Win32 and BeOS specific modules -were disabled, while modules that use Unix APIs and /dev/random are -built. +The ones that are 'loaded on request only' have to be explicitly asked +for, because they rely on third party libraries which your system +might not have. For instance to enable zlib support, add +\verb|--with-zlib| to your invocation of \verb|configure.py|. You can control which algorithms and modules are built using the options ``\verb|--enable-modules=MODS|'' and @@ -104,20 +101,6 @@ options ``\verb|--enable-modules=MODS|'' and Modules not listed on the command line will simply be loaded if needed or if configured to load by default. -Not all OSes or CPUs have specific support in -\filename{configure.pl}. If the CPU architecture of your system isn't -supported by \filename{configure.pl}, use 'generic'. This setting -disables machine-specific optimization flags. Similarly, setting OS to -'generic' disables things which depend greatly on OS support -(specifically, shared libraries). - -However, it's impossible to guess which options to give to a system -compiler. Thus, if you want to compile Botan with a compiler which -\filename{configure.pl} does not support, you will need to tell it how -that compiler works. This is done by adding a new file in the -directory \filename{src/build-data/cc}; the existing files should put you -in the right direction. - The script tries to guess what kind of makefile to generate, and it almost always guesses correctly (basically, Visual C++ uses NMAKE with Windows commands, and everything else uses Unix make with POSIX @@ -128,14 +111,12 @@ commonly used by Windows compilers. To add a new variant (eg, a build script for VMS), you will need to create a new template file in \filename{src/build-data/makefile}. -\pagebreak - \subsection{POSIX / Unix} The basic build procedure on Unix and Unix-like systems is: \begin{verbatim} - $ ./configure.pl [--enable-modules=<list>] [--cc=CC] + $ ./configure.py [--enable-modules=<list>] [--cc=CC] $ make # You may need to set your LD_LIBRARY_PATH or equivalent for ./check to run $ make check # optional, but a good idea @@ -148,9 +129,9 @@ found within your PATH. The \verb|make install| target has a default directory in which it will install Botan (typically \verb|/usr/local|). You can override this by using the \texttt{--prefix} argument to -\filename{configure.pl}, like so: +\filename{configure.py}, like so: -\verb|./configure.pl --prefix=/opt <other arguments>| +\verb|./configure.py --prefix=/opt <other arguments>| On some systems shared libraries might not be immediately visible to the runtime linker. For example, on Linux you may have to edit @@ -163,10 +144,10 @@ to include the directory that the Botan libraries were installed into. The situation is not much different here. We'll assume you're using Visual C++ (for Cygwin, the Unix instructions are probably more relevant). You need to -have a copy of Perl installed, and have both Perl and Visual C++ in your path. +have a copy of Python installed, and have both Python and Visual C++ in your path. \begin{verbatim} - > perl configure.pl --cc=msvc (or --cc=gcc for MinGW) [--cpu=CPU] + > python configure.py --cc=msvc (or --cc=gcc for MinGW) [--cpu=CPU] > nmake > nmake check # optional, but recommended \end{verbatim} @@ -244,7 +225,7 @@ environment in a different directory. \subsection{Local Configuration} You may want to do something peculiar with the configuration; to -support this there is a flag to \filename{configure.pl} called +support this there is a flag to \filename{configure.py} called \texttt{--with-local-config=<file>}. The contents of the file are inserted into \filename{build/build.h} which is (indirectly) included into every Botan header and source file. @@ -395,4 +376,70 @@ is less of a problem - only the developer needs to worry about it. As long as they can remember where they installed Botan, they just have to set the appropriate flags in their Makefile/project file. +\pagebreak + +\section{Language Wrappers} + +\subsection{Building the Python wrappers} + +The Python wrappers for Botan use Boost.Python, so you must have Boost +installed. To build the wrappers, add the flag + +\verb|--use-boost-python| + +to \verb|configure.py|. This will create a second makefile, +\verb|Makefile.python|, with instructions for building the Python +module. After building the library, execute + +\begin{verbatim} +$ make -f Makefile.python +\end{verbatim} + +to build the module. Currently only Unix systems are supported, and +the Makefile assumes that the version of Python you want to build +against is the same one you used to run \verb|configure.py|. + +To install the module, use the \verb|install| target. + +Examples of using the Python module can be seen in \filename{doc/python} + +\subsection{Building the Perl XS wrappers} + +To build the Perl XS wrappers, change your directory to +\filename{src/wrap/perl-xs} and run \verb|perl Makefile.PL|, then run +\verb|make| to build the module and \verb|make test| to run the test +suite. + +\begin{verbatim} +$ perl Makefile.PL +Checking if your kit is complete... +Looks good +Writing Makefile for Botan +$ make +cp Botan.pm blib/lib/Botan.pm +AutoSplitting blib/lib/Botan.pm (blib/lib/auto/Botan) +/usr/bin/perl5.8.8 /usr/lib64/perl5/5.8.8/ExtUtils/xsubpp [...] +g++ -c -Wno-write-strings -fexceptions -g [...] +Running Mkbootstrap for Botan () +chmod 644 Botan.bs +rm -f blib/arch/auto/Botan/Botan.so +g++ -shared Botan.o -o blib/arch/auto/Botan/Botan.so \ + -lbotan -lbz2 -lpthread -lrt -lz \ + +chmod 755 blib/arch/auto/Botan/Botan.so +cp Botan.bs blib/arch/auto/Botan/Botan.bs +chmod 644 blib/arch/auto/Botan/Botan.bs +Manifying blib/man3/Botan.3pm +$ make test +PERL_DL_NONLAZY=1 /usr/bin/perl5.8.8 [...] +t/base64......ok +t/filt........ok +t/hex.........ok +t/oid.........ok +t/pipe........ok +t/x509cert....ok +All tests successful. +Files=6, Tests=83, 0 wallclock secs ( 0.08 cusr + 0.02 csys = 0.10 CPU) +\end{verbatim} + \end{document} diff --git a/doc/examples/bench.cpp b/doc/examples/bench.cpp index 37ef1104d..cc43fade0 100644 --- a/doc/examples/bench.cpp +++ b/doc/examples/bench.cpp @@ -7,26 +7,6 @@ using namespace Botan; #include <iostream> -double best_speed(const std::string& algorithm, - u32bit milliseconds, - RandomNumberGenerator& rng, - Timer& timer) - { - std::map<std::string, double> speeds = - algorithm_benchmark(algorithm, milliseconds, - timer, rng, - global_state().algorithm_factory()); - - double best_time = 0; - - for(std::map<std::string, double>::const_iterator i = speeds.begin(); - i != speeds.end(); ++i) - if(i->second > best_time) - best_time = i->second; - - return best_time; - } - const std::string algos[] = { "AES-128", "AES-192", @@ -62,7 +42,6 @@ const std::string algos[] = { "FORK-256", "GOST-34.11", "HAS-160", - "HAS-V", "MD2", "MD4", "MD5", @@ -81,18 +60,40 @@ const std::string algos[] = { "", }; -int main() +void benchmark_algo(const std::string& algo, + RandomNumberGenerator& rng) + { + u32bit milliseconds = 3000; + Default_Benchmark_Timer timer; + Algorithm_Factory& af = global_state().algorithm_factory(); + + std::map<std::string, double> speeds = + algorithm_benchmark(algo, milliseconds, timer, rng, af); + + std::cout << algo << ":"; + + for(std::map<std::string, double>::const_iterator i = speeds.begin(); + i != speeds.end(); ++i) + { + std::cout << " " << i->second << " [" << i->first << "]"; + } + std::cout << "\n"; + } + +int main(int argc, char* argv[]) { LibraryInitializer init; - u32bit milliseconds = 1000; AutoSeeded_RNG rng; - Default_Benchmark_Timer timer; - for(u32bit i = 0; algos[i] != ""; ++i) + if(argc == 1) // no args, benchmark everything + { + for(u32bit i = 0; algos[i] != ""; ++i) + benchmark_algo(algos[i], rng); + } + else { - std::string algo = algos[i]; - std::cout << algo << ' ' - << best_speed(algo, milliseconds, rng, timer) << "\n"; + for(int i = 1; argv[i]; ++i) + benchmark_algo(argv[i], rng); } } diff --git a/doc/examples/cpuid.cpp b/doc/examples/cpuid.cpp new file mode 100644 index 000000000..59940b500 --- /dev/null +++ b/doc/examples/cpuid.cpp @@ -0,0 +1,15 @@ +#include <stdio.h> + +#include <botan/cpuid.h> + +using namespace Botan; + +int main() + { + printf("Cache line size: %d\n", CPUID::cache_line_size()); + printf("RDTSC: %d\n", CPUID::has_rdtsc()); + printf("SSE2 %d\n", CPUID::has_sse2()); + printf("SSSE3 %d\n", CPUID::has_ssse3()); + printf("SSE41 %d\n", CPUID::has_sse41()); + printf("SSE42 %d\n", CPUID::has_sse42()); + } diff --git a/doc/examples/package.cpp b/doc/examples/package.cpp new file mode 100644 index 000000000..981abaa31 --- /dev/null +++ b/doc/examples/package.cpp @@ -0,0 +1,61 @@ + +#include <botan/botan.h> +#include <botan/serpent.h> +#include <botan/package.h> + +#include <iostream> +#include <fstream> +#include <vector> + +using namespace Botan; + +std::vector<byte> slurp_file(const std::string& filename) + { + std::ifstream in(filename.c_str()); + + std::vector<byte> out; + byte buf[4096] = { 0 }; + + while(in.good()) + { + in.read((char*)buf, sizeof(buf)); + ssize_t got = in.gcount(); + + out.insert(out.end(), buf, buf+got); + } + + return out; + } + +int main(int argc, char* argv[]) + { + if(argc != 2) + { + std::cout << "Usage: " << argv[0] << " filename\n"; + return 1; + } + + LibraryInitializer init; + + AutoSeeded_RNG rng; + + BlockCipher* cipher = new Serpent; + + std::vector<byte> input = slurp_file(argv[1]); + std::vector<byte> output(input.size() + cipher->BLOCK_SIZE); + + AllOrNothingTransform::package(rng, new Serpent, + &input[0], input.size(), + &output[0]); + + std::vector<byte> unpackage_output(output.size() - cipher->BLOCK_SIZE); + + AllOrNothingTransform::unpackage(new Serpent, + &output[0], output.size(), + &unpackage_output[0]); + + if(unpackage_output == input) + std::cout << Package/unpackage worked\n"; + else + std::cout << "Something went wrong :(\n"; + } diff --git a/doc/examples/tss.cpp b/doc/examples/tss.cpp new file mode 100644 index 000000000..1881ffe24 --- /dev/null +++ b/doc/examples/tss.cpp @@ -0,0 +1,38 @@ +#include <botan/botan.h> +#include <botan/tss.h> +#include <iostream> +#include <stdio.h> + +namespace { + +void print(const Botan::SecureVector<Botan::byte>& r) + { + for(Botan::u32bit i = 0; i != r.size(); ++i) + printf("%02X", r[i]); + printf("\n"); + } + +} + +int main() + { + using namespace Botan; + + LibraryInitializer init; + + AutoSeeded_RNG rng; + + byte id[16]; + for(int i = 0; i != 16; ++i) + id[i] = i; + + const byte S2[] = { 0xDE, 0xAD, 0xCA, 0xFE, 0xBA, 0xBE, 0xBE, 0xEF }; + + std::vector<RTSS_Share> shares = + RTSS_Share::split(4, 6, S2, sizeof(S2), id, rng); + + for(size_t i = 0; i != shares.size(); ++i) + std::cout << i << " = " << shares[i].to_string() << "\n"; + + print(RTSS_Share::reconstruct(shares)); + } diff --git a/doc/log.txt b/doc/log.txt index 7183bd8b6..9ebf3a2ad 100644 --- a/doc/log.txt +++ b/doc/log.txt @@ -1,4 +1,24 @@ +* 1.9.1-rc1, 2009-10-09 + - Better support for Python and Perl wrappers + - Add an implementation of Blue Midnight Wish (Round 2 tweak version) + - Add threshold secret sharing (draft-mcgrew-tss-02) + - Add runtime cpu feature detection for x86/x86-64 + - Add code for general runtime self testing for hashes, MACs, and ciphers + - Add support for GNU/Hurd + - New parsing code for SCAN algorithm names + - Alter Skein-512 to match the tweaked 1.2 specification + - Enable SSE2 optimizations under Visual C++ + +* 1.9.0, 2009-09-09 + - Add support for parallel invocation of block ciphers where possible + - Add SSE2 implementation of Serpent + - Add Rivest's package transform (an all or nothing transform) + - Minor speedups to the Turing key schedule + - Fix processing multiple messages in XTS mode + - Add --no-autoload option to configure.py, for minimized builds + - The previously used configure.pl script is no longer supported + * 1.8.8-dev, 2009-??-?? - Alter Skein-512 to match the tweaked 1.2 specification diff --git a/doc/python.tex b/doc/python.tex new file mode 100644 index 000000000..afdd66b6a --- /dev/null +++ b/doc/python.tex @@ -0,0 +1,68 @@ +\documentclass{article} + +\setlength{\textwidth}{6.5in} % 1 inch side margins +\setlength{\textheight}{9in} % ~1 inch top and bottom margins + +\setlength{\headheight}{0in} +\setlength{\topmargin}{0in} +\setlength{\headsep}{0in} + +\setlength{\oddsidemargin}{0in} +\setlength{\evensidemargin}{0in} + +\title{\textbf{Botan Python Interface Documentation}} +\author{Jack Lloyd \\ + \texttt{[email protected]}} +\date{2009/10/10} + +\newcommand{\filename}[1]{\texttt{#1}} +\newcommand{\manpage}[2]{\texttt{#1}(#2)} + +\newcommand{\macro}[1]{\texttt{#1}} + +\newcommand{\function}[1]{\textbf{#1}} +\newcommand{\type}[1]{\texttt{#1}} +\renewcommand{\arg}[1]{\textsl{#1}} +\newcommand{\variable}[1]{\textsl{#1}} + +\begin{document} + +\maketitle + +\tableofcontents + +\parskip=5pt +\pagebreak + +\section{Ciphers} + +Botan's Python interface provides a generic interface to any cipher +supported by the library. The class \type{botan.Cipher} takes three +arguments, all strings: first, the name of the algorith, second the +direction (which can be either ``encrypt'' or ``decrypt''), and +lastly, the key to use. For instance + +\begin{verbatim} + encryptor = botan.Cipher("AES-128/EAX", "encrypt", key) +\end{verbatim} + +creates an object that will encrypt and authenticate messages using +the EAX mode of operation using the AES cipher. To use this object, +call the \function{cipher} function with two arguments - the input +to encrypt, and the IV to use: + +\begin{verbatim} + ciphertext = encryptor.cipher(input, salt) +\end{verbatim} + + +\subsection{Cryptobox} + + +\subsection{RNGs} + +\section{RSA} + + + +\end{document} diff --git a/doc/python/cipher.py b/doc/python/cipher.py new file mode 100755 index 000000000..1be2759ae --- /dev/null +++ b/doc/python/cipher.py @@ -0,0 +1,44 @@ +#!/usr/bin/python + +import botan +import sys + +def encrypt(input, passphrase): + rng = botan.RandomNumberGenerator() + + # Use as both EAX IV and PBKDF2 salt + salt = rng.gen_random(10) + + iterations = 10000 + output_size = 16 + + key = botan.pbkdf2(passphrase, salt, iterations, output_size, "SHA-1") + + encryptor = botan.Cipher("AES-128/EAX", "encrypt", key) + + ciphertext = encryptor.cipher(input, salt) + return (ciphertext, salt) + +def decrypt(input, salt, passphrase): + iterations = 10000 + output_size = 16 + + key = botan.pbkdf2(passphrase, salt, iterations, output_size, "SHA-1") + + decryptor = botan.Cipher("AES-128/EAX", "decrypt", key) + + return decryptor.cipher(input, salt) + +def main(args = None): + if args is None: + args = sys.argv + + passphrase = args[1] + input = ''.join(open(args[2]).readlines()) + + (ciphertext, salt) = encrypt(input, passphrase) + + print decrypt(ciphertext, salt, passphrase) + +if __name__ == '__main__': + sys.exit(main()) diff --git a/doc/python/cryptobox.py b/doc/python/cryptobox.py new file mode 100755 index 000000000..1968b40e1 --- /dev/null +++ b/doc/python/cryptobox.py @@ -0,0 +1,34 @@ +#!/usr/bin/python + +import sys +import botan + +def main(args = None): + if args is None: + args = sys.argv + + if len(args) != 3: + raise Exception("Bad usage") + + password = args[1] + input = ''.join(open(args[2]).readlines()) + + rng = botan.RandomNumberGenerator() + + ciphertext = botan.cryptobox_encrypt(input, password, rng) + + print ciphertext + + plaintext = '' + + try: + plaintext = botan.cryptobox_decrypt(ciphertext, password + 'FAIL') + except Exception, e: + print "Oops -- ", e + + plaintext = botan.cryptobox_decrypt(ciphertext, password) + + print plaintext + +if __name__ == '__main__': + sys.exit(main()) diff --git a/doc/python/nisttest.py b/doc/python/nisttest.py new file mode 100755 index 000000000..3ea8fda0f --- /dev/null +++ b/doc/python/nisttest.py @@ -0,0 +1,61 @@ +#!/usr/bin/python + +import sys, os, botan +from os.path import join; + +def validate(ca_certs, certs, crls, ee_certs): + store = botan.X509_Store() + for cert in certs: + if cert not in ee_certs: + store.add_cert(botan.X509_Certificate(cert), cert in ca_certs) + + for crl in crls: + r = store.add_crl(botan.X509_CRL(crl)) + if r != botan.verify_result.verified: + return r + + for ee in ee_certs: + r = store.validate(botan.X509_Certificate(ee)) + if r != botan.verify_result.verified: + return r + + return botan.verify_result.verified + +def run_test(files, rootdir, testname, expected): + crls = [join(rootdir,x) for x in files if x.endswith(".crl")] + certs = [join(rootdir,x) for x in files if x.endswith(".crt")] + end_entity = [x for x in certs if x.find("end.crt") != -1] + ca_certs = [x for x in certs if x.find("root.crt") != -1] + + print "%s..." % testname, + + result = validate(ca_certs, certs, crls, end_entity) + result = repr(result).replace('botan._botan.verify_result.', '') + + if result != expected: + print "FAILED: got %s, expected %s" % (result, expected) + else: + print "passed" + +def main(): + def load_results(file): + results = {} + for line in open(file, 'r'): + line = line[0:line.find('#')].strip() + if line: + test,result = line.split(' ') + results[test] = result + return results + + results = load_results('results.txt') + + for root, dirs, files in os.walk('../../checks/nist_tests/tests'): + if files: + thistest = root[root.rfind('/')+1:] + if thistest in results: + run_test(files, root, thistest, results[thistest]) + else: + print "%s... skipping - no expected result set" % thistest + +if __name__ == "__main__": + sys.exit(main()) diff --git a/doc/python/results.txt b/doc/python/results.txt new file mode 100644 index 000000000..7a3824001 --- /dev/null +++ b/doc/python/results.txt @@ -0,0 +1,60 @@ +# This is the file of expected results for nisttest.py +test01 verified +test02 signature_error +test03 signature_error +test04 verified +test05 cert_not_yet_valid +test06 cert_not_yet_valid +test07 verified +test08 cert_not_yet_valid +test09 cert_has_expired +test10 cert_has_expired +test11 cert_has_expired +test12 verified +test13 cert_issuer_not_found +test14 cert_issuer_not_found +test15 verified +test16 verified +test17 verified +test18 verified +# changed; should be no_revocation_data_available, but I don't want to +# force people to use CRLs +test19 verified +test20 cert_is_revoked +test21 cert_is_revoked +test22 ca_cert_not_for_cert_issuer +test23 ca_cert_not_for_cert_issuer +test24 verified +test25 ca_cert_not_for_cert_issuer +test26 verified +test27 verified +test28 ca_cert_not_for_cert_issuer +test29 ca_cert_not_for_cert_issuer +test30 verified +test31 ca_cert_not_for_crl_issuer +test32 ca_cert_not_for_crl_issuer +test33 verified +test54 cert_chain_too_long +test55 cert_chain_too_long +test56 verified +test57 verified +test58 cert_chain_too_long +test59 cert_chain_too_long +test60 cert_chain_too_long +test61 cert_chain_too_long +test62 verified +test63 verified +test64 signature_error +# changed; I have no idea why this test is supposed to fail +test65 verified +test66 crl_issuer_not_found +# changed; one of the CRLs has an unknown creator, so we fail +# prior to getting to the end-entity check +test67 crl_issuer_not_found +test68 cert_is_revoked +test69 cert_is_revoked +test70 cert_is_revoked +test71 cert_is_revoked +test72 crl_has_expired +test73 crl_has_expired +test74 verified diff --git a/doc/python/rng_test.py b/doc/python/rng_test.py new file mode 100755 index 000000000..06c79b84e --- /dev/null +++ b/doc/python/rng_test.py @@ -0,0 +1,22 @@ +#!/usr/bin/python + +import botan + +rng = botan.RandomNumberGenerator() + +print "name", rng.name() + +rng.add_entropy("blah") + +print "random 16", rng.gen_random(16).encode("hex") +print "random 32", rng.gen_random(32).encode("base64"), + +rng.reseed() + +for i in range(0, 10): + print rng.gen_random_byte(), +print + +rng.add_entropy("blah") + +print "random 16", rng.gen_random(16).encode("hex") diff --git a/doc/python/rsa.py b/doc/python/rsa.py new file mode 100755 index 000000000..15ffcffa3 --- /dev/null +++ b/doc/python/rsa.py @@ -0,0 +1,31 @@ +#!/usr/bin/python + +import botan + +rng = botan.RandomNumberGenerator() + +rsa_priv = botan.RSA_PrivateKey(768, rng) + +print rsa_priv.to_string() +print int(rsa_priv.get_N()) +print int(rsa_priv.get_E()) + + +rsa_pub = botan.RSA_PublicKey(rsa_priv) + +key = rng.gen_random(20) + +ciphertext = rsa_pub.encrypt(key, 'EME1(SHA-1)', rng) + +print ciphertext.encode('hex') + +plaintext = rsa_priv.decrypt(ciphertext, 'EME1(SHA-1)') + +print plaintext == key + + +signature = rsa_priv.sign(key, 'EMSA4(SHA-256)', rng) + +key = key.replace('a', 'b') + +print rsa_pub.verify(key, signature, 'EMSA4(SHA-256)') diff --git a/doc/scripts/configure.pl b/doc/scripts/configure.pl new file mode 100755 index 000000000..9415d5a42 --- /dev/null +++ b/doc/scripts/configure.pl @@ -0,0 +1,2340 @@ +#!/usr/bin/perl -w + +require 5.006; + +use strict; + +use Config; +use Getopt::Long; +use File::Spec; +use File::Copy; +use File::Find; +use Sys::Hostname; + +my $MAJOR_VERSION = 1; +my $MINOR_VERSION = 8; +my $PATCH_VERSION = 7; + +my $VERSION_SUFFIX = ''; + +my $SO_PATCH_VERSION = 2; + +my $VERSION_STRING = "$MAJOR_VERSION.$MINOR_VERSION.$PATCH_VERSION$VERSION_SUFFIX"; +my $SO_VERSION_STRING = "$MAJOR_VERSION.$MINOR_VERSION.$SO_PATCH_VERSION$VERSION_SUFFIX"; + +################################################## +# Data # +################################################## +my (%CPU, %OPERATING_SYSTEM, %COMPILER, %MODULES); + +my @DOCS = ( + 'api.pdf', 'tutorial.pdf', 'fips140.pdf', + 'api.tex', 'tutorial.tex', 'fips140.tex', + 'credits.txt', 'license.txt', 'log.txt', + 'thanks.txt', 'todo.txt', 'pgpkeys.asc'); + +my $TRACING = 0; + +################################################## +# Run main() and Quit # +################################################## +my $config = {}; + +main(); +exit; + +sub exec_uname { + # Only exec it if we think it might actually work + if(-f '/bin/uname' || -f '/usr/bin/uname' || -f '/bin/sh') { + my $uname = `uname -a`; + if($uname) { + chomp $uname; + return $uname; + } + } + + return ''; +} + +sub deprecation_warning { + warning("$0 is deprecated; migration to ./configure.py strongly recommended"); +} + +################################################## +# Main Driver # +################################################## +sub main { + my $base_dir = where_am_i(); + + deprecation_warning(); + + $$config{'uname'} = exec_uname(); + + $$config{'base-dir'} = $base_dir; + $$config{'src-dir'} = File::Spec->catdir($base_dir, 'src'); + $$config{'checks-dir'} = File::Spec->catdir($base_dir, 'checks'); + $$config{'doc_src_dir'} = File::Spec->catdir($base_dir, 'doc'); + + $$config{'config-dir'} = + File::Spec->catdir($$config{'src-dir'}, 'build-data'); + + $$config{'command_line'} = $0 . ' ' . join(' ', @ARGV); + $$config{'timestamp'} = gmtime; + $$config{'user'} = getlogin || getpwuid($<) || ''; + $$config{'hostname'} = hostname; + + %CPU = read_info_files($config, 'arch', \&get_arch_info); + %OPERATING_SYSTEM = read_info_files($config, 'os', \&get_os_info); + %COMPILER = read_info_files($config, 'cc', \&get_cc_info); + %MODULES = read_module_files($config); + + add_to($config, { + 'version_major' => $MAJOR_VERSION, + 'version_minor' => $MINOR_VERSION, + 'version_patch' => $PATCH_VERSION, + 'version' => $VERSION_STRING, + 'so_version' => $SO_VERSION_STRING, + }); + + get_options($config); + + my $default_value_is = sub { + my ($var, $val) = @_; + $$config{$var} = $val if not defined($$config{$var}); + }; + + &$default_value_is('gcc_bug', 0); + &$default_value_is('autoconfig', 1); + &$default_value_is('debug', 0); + &$default_value_is('shared', 'yes'); + &$default_value_is('local_config', ''); + + # Goes into build-specific dirs (maybe) + + $$config{'build_dir'} = 'build'; + $$config{'botan_config'} = File::Spec->catfile( + $$config{'build_dir'}, 'botan-config'); + + $$config{'botan_pkgconfig'} = File::Spec->catfile( + $$config{'build_dir'}, + 'botan-' . $MAJOR_VERSION . '.' . $MINOR_VERSION . '.pc'); + + $$config{'makefile'} = 'Makefile'; + $$config{'check_prefix'} = ''; + $$config{'lib_prefix'} = ''; + + if(defined($$config{'with_build_dir'})) { + for my $var ('build_dir', + 'botan_config', + 'botan_pkgconfig', + 'makefile', + 'check_prefix', + 'lib_prefix') + { + $$config{$var} = File::Spec->catfile($$config{'with_build_dir'}, + $$config{$var}); + } + } + else { + } + + choose_target($config); + + my $os = $$config{'os'}; + my $cc = $$config{'compiler'}; + + &$default_value_is('prefix', os_info_for($os, 'install_root')); + &$default_value_is('libdir', os_info_for($os, 'lib_dir')); + &$default_value_is('docdir', os_info_for($os, 'doc_dir')); + &$default_value_is('make_style', $COMPILER{$cc}{'makefile_style'}); + + scan_modules($config); + + print_enabled_modules($config); + + add_to($config, { + 'includedir' => os_info_for($os, 'header_dir'), + + 'build_lib' => File::Spec->catdir($$config{'build_dir'}, 'lib'), + 'build_check' => File::Spec->catdir($$config{'build_dir'}, 'checks'), + 'build_include' => + File::Spec->catdir($$config{'build_dir'}, 'include'), + 'build_include_botan' => + File::Spec->catdir($$config{'build_dir'}, 'include', 'botan'), + + 'mp_bits' => find_mp_bits($config), + 'mod_libs' => [ using_libs($config) ], + + 'sources' => { }, + 'includes' => { }, + + 'check_src' => { + map_to($$config{'checks-dir'}, + grep { $_ ne 'keys' and !m@\.(dat|h)$@ } + dir_list($$config{'checks-dir'})) + } + }); + + load_modules($config); + + my @dirs = mkdirs($$config{'build_dir'}, + $$config{'build_include'}, + $$config{'build_include_botan'}, + $$config{'build_lib'}, + $$config{'build_check'}); + + #autoconfig('Created ' . join(' ', @dirs)) if @dirs; + + write_pkg_config($config); + + determine_config($config); + + process_template(File::Spec->catfile($$config{'config-dir'}, 'buildh.in'), + File::Spec->catfile($$config{'build_dir'}, 'build.h'), + $config); + + process_template(File::Spec->catfile( + $$config{'config-dir'}, 'botan.doxy.in'), + File::Spec->catfile($$config{'doc_src_dir'}, 'botan.doxy'), + $config); + + $$config{'includes'}{'build.h'} = $$config{'build_dir'}; + + generate_makefile($config); + + copy_include_files($config); + + deprecation_warning(); +} + +sub where_am_i { + my ($volume,$dir,$file) = File::Spec->splitpath($0); + my $src_dir = File::Spec->catpath($volume, $dir, ''); + return $src_dir if $src_dir; + return File::Spec->curdir(); +} + +################################################## +# Diagnostics # +################################################## +sub with_diagnostic { + my ($type, @args) = @_; + + my $args = join('', @args); + my $str = "($type): "; + while(length($str) < 14) { $str = ' ' . $str; } + + $str .= $args . "\n"; + return $str; +} + +sub croak { + die with_diagnostic('error', @_); +} + +sub warning { + warn with_diagnostic('warning', @_); +} + +sub autoconfig { + print with_diagnostic('autoconfig', @_); +} + +sub emit_help { + print join('', @_); + exit; +} + +sub trace { + return unless $TRACING; + + my (undef, undef, $line) = caller(0); + my (undef, undef, undef, $func) = caller(1); + + $func =~ s/main:://; + + print with_diagnostic('trace', "at $func:$line - ", @_); +} + +################################################## +# Display Help and Quit # +################################################## +sub display_help { + sub module_sets { + my %modsets; + for my $name (sort keys %MODULES) { + my %info = %{$MODULES{$name}}; + next unless (defined($info{'modset'})); + + for my $s (split(/,/, $info{'modset'})) { + $modsets{$s} = undef; + } + } + + return sort keys %modsets; + } + + my $sets = join(' ', module_sets()); + + my $listing = sub { + my (@list) = @_; + + return '' if (@list == 0); + + my ($output, $len) = ('', 0); + + my $append = sub { + my ($to_append) = @_; + $output .= $to_append; + $len += length $to_append; + }; + + foreach my $name (sort @list) { + next if $name eq 'defaults'; + if($len > 58) { + $output .= "\n "; + $len = 8; + } + &$append($name . ' '); + } + chop $output; + return $output; + }; + + #my $modules = &$listing(keys %MODULES); + my $compilers = &$listing(keys %COMPILER); + my $oses = &$listing(keys %OPERATING_SYSTEM); + my $cpus = &$listing(keys %CPU); + + my $helptxt = <<ENDOFHELP; +This is $0 from Botan $VERSION_STRING + +To select the compiler, use + + --cc=[$compilers] + +To select the OS and processor to target, use these options. By +default, autodetection will be attempted. + + --os=[generic $oses] + --cpu=[generic $cpus] + + --with-endian=[little big none] + --with-unaligned-mem=[yes no] + +To change build options: + + --with-tr1={none,system,boost} enable (or disable) using a TR1 implementation + --with-build-dir=DIR setup the build in DIR + --with-local-config=FILE include the contents of FILE into build.h + + --disable-debug don't worry about debugging + --enable-debug set compiler flags for debugging + + --enable-shared enable shared libraries + --disable-shared don't build shared libararies + +To change where the library is installed: + + --prefix=PATH set the base installation directory + --libdir=PATH install library files in \${prefix}/\${libdir} + --docdir=PATH install documentation in \${prefix}/\${docdir} + +To change what modules to use: + + --enable-modules=[module,[module[,...]]] + --disable-modules=[module,[module[,...]]] + +To get diagnostic and debug output: + + --module-info display more information about modules + + --show-arch-info=CPU show more information about a particular CPU + [$cpus] + + --help display this help + --version display the version of Botan + --quiet display only warnings and errors + --trace enable runtime tracing of this program + +See doc/building.pdf for more information about this program. + +ENDOFHELP + + emit_help($helptxt); +} + +################################################## +# Display Further Information about Modules # +################################################## +sub module_info { + + my $info = ''; + foreach my $mod (sort keys %MODULES) { + my $modinfo = $MODULES{$mod}; + my $fullname = $$modinfo{'realname'}; + + while(length($mod) < 10) { $mod .= ' '; } + $info .= "$mod - $fullname\n"; + } + + return $info; +} + +################################################## +# +################################################## +sub choose_target { + my ($config) = @_; + + my $cc = $$config{'compiler'}; + my $os = $$config{'os'}; + my $cpu = $$config{'cpu'}; + + autoconfig("Setting up build for Botan $VERSION_STRING"); + + $cpu = guess_cpu() if not defined($cpu); + $cc = guess_compiler() if not defined($cc); + $os = guess_os() if not defined($os); + + display_help() + unless(defined($cc) and defined($os) and defined($cpu)); + + croak("Compiler $cc isn't known (try --help)") + unless defined($COMPILER{$cc}); + + my %ccinfo = %{$COMPILER{$cc}}; + + if(defined($ccinfo{'compiler_has_tr1'})) { + unless(defined($$config{'tr1'})) { + autoconfig('Assuming compiler ', $cc, ' has TR1 headers. ', + 'Use --with-tr1=none to disable'); + $$config{'tr1'} = 'system'; + } + } + + $os = os_alias($os); + croak("OS $os isn't known (try --help)") unless + ($os eq 'generic' or defined($OPERATING_SYSTEM{$os})); + + my ($arch, $submodel) = figure_out_arch($cpu); + + # hacks + if($cc eq 'gcc') { + $ccinfo{'binary_name'} = 'c++' if($os eq 'darwin'); + + if($$config{'gcc_bug'} != 1) { + my $binary = $ccinfo{'binary_name'}; + + my $gcc_version = `$binary -v 2>&1`; + + $gcc_version = '' if not defined $gcc_version; + + my $has_ll_bug = 0; + $has_ll_bug = 1 if($gcc_version =~ /4\.[01234]/); + $has_ll_bug = 1 if($gcc_version =~ /3\.[34]/); + $has_ll_bug = 1 if($gcc_version =~ /2\.95\.[0-4]/); + $has_ll_bug = 1 if($gcc_version eq ''); + + $has_ll_bug = 0 if($arch eq 'alpha' or $arch =~ /.*64$/); + + if($has_ll_bug) + { + warning('Enabling -fpermissive to work around ', + 'possible GCC bug'); + + $$config{'gcc_bug'} = 1; + } + + warning('GCC 2.95.x issues many spurious warnings') + if($gcc_version =~ /2\.95\.[0-4]/); + } + } + + trace("using $cc $os $arch $submodel"); + + add_to($config, { + 'compiler' => $cc, + 'os' => $os, + 'arch' => $arch, + 'submodel' => $submodel, + }); +} + +sub module_runs_on { + my ($config, $modinfo, $mod, $noisy) = @_; + + my $cc = $$config{'compiler'}; + my $os = $$config{'os'}; + my $submodel = $$config{'submodel'}; + my $arch = $$config{'arch'}; + + my %modinfo = %{$modinfo}; + + my $realname = $modinfo{'realname'}; + + my @arch_list = @{ $modinfo{'arch'} }; + if(scalar @arch_list > 0 && !in_array($arch, \@arch_list) && + !in_array($submodel, \@arch_list)) + { + autoconfig("$mod ($realname): skipping, " . + "not compatible with " . realname($arch) . + "/" . $submodel) if $noisy; + return 0; + } + + my @os_list = @{ $modinfo{'os'} }; + if(scalar @os_list > 0 && !in_array($os, \@os_list)) + { + autoconfig("$mod ($realname): " . + "skipping, not compatible with " . realname($os)) if $noisy; + return 0; + } + + my @cc_list = @{ $modinfo{'cc'} }; + if(scalar @cc_list > 0 && !in_array($cc, \@cc_list)) { + autoconfig("$mod ($realname): " . + "skipping, not compatible with " . realname($cc)) if $noisy; + return 0; + } + + + if($modinfo{'uses_tr1'} eq 'yes') { + return 0 unless defined($$config{'tr1'}); + + my $tr1 = $$config{'tr1'}; + return 0 unless($tr1 eq 'system' or $tr1 eq 'boost'); + } + + return 1; +} + +sub scan_modules { + my ($config) = @_; + + foreach my $mod (sort keys %MODULES) { + my %modinfo = %{ $MODULES{$mod} }; + + next if(defined($$config{'modules'}{$mod}) && $$config{'modules'}{$mod} < 0); + + next unless(module_runs_on($config, \%modinfo, $mod, 0)); + + if($modinfo{'load_on'} eq 'auto' or + $modinfo{'load_on'} eq 'always' or + ($modinfo{'load_on'} eq 'asm_ok' and $$config{'asm_ok'})) { + + my %maybe_load = (); + my $all_deps_found = 1; + + LINE: foreach (@{$modinfo{'requires'}}) { + for my $req_mod (split(/\|/, $_)) { + next unless defined $MODULES{$req_mod}; + + next if(defined($$config{'modules'}{$req_mod}) && $$config{'modules'}{$req_mod} < 0); + next unless(module_runs_on($config, $MODULES{$req_mod}, $req_mod, 0)); + + $maybe_load{$req_mod} = 1; + next LINE; + } + $all_deps_found = 0; + } + + if($all_deps_found) { + foreach my $depmod (keys %maybe_load) + { $$config{'modules'}{$depmod} = 1; } + $$config{'modules'}{$mod} = 1; + } + } + } +} + +sub print_enabled_modules { + my ($config) = @_; + + return unless($$config{'verbose'}); + + my %by_type; + + foreach my $mod (sort keys %MODULES) { + my $type = $MODULES{$mod}{'type'}; + + my $n = 0; + $n = 1 if($$config{'modules'}{$mod} && $$config{'modules'}{$mod} > 0); + + $by_type{$type}{$mod} = $n; + } + + for my $type (sort keys %by_type) { + my %mods = %{$by_type{$type}}; + + my @load_lines; + + if(keys %mods == 1) { + my $on = $mods{$type}; + + if($on > 0) { + print with_diagnostic('loading', $type); + } + else { + print with_diagnostic('loading', '[', $type , ']'); + } + } + else { + my $s = $type . ': '; + + for my $mod (sort keys %mods) { + my $on = $mods{$mod}; + + if($s eq '') { + $s = ' ' x (length($type) + 16); + } + + if($on > 0) { + $s .= $mod . ' '; + } + else { + $s .= '[' . $mod . '] '; + } + + if(length($s) > 60) { + push @load_lines, $s; + $s = ''; + } + } + + #print "Last1 '$s'\n"; + + $s =~ s/\s*$//m; # strip trailing whitespace + + push @load_lines, $s if($s ne ''); + + + print with_diagnostic('loading', join("\n", @load_lines)); + } + } +} + +sub get_options { + my ($config) = @_; + + my $save_option = sub { + my ($opt, $val) = @_; + $opt =~ s/-/_/g; + $$config{$opt} = $val; + }; + + $$config{'verbose'} = 1; + $$config{'asm_ok'} = 1; + $$config{'tr1'} = undef; # not enabled by default + $$config{'modules'} = {}; + + sub arch_info { + my $arg = $_[0]; + + my $arch = find_arch($arg); + + unless(defined($arch) and defined($CPU{$arch})) { + warning("Unknown arch '$arg' passed to --arch-info (try --help)"); + return ''; + } + + my %info = %{ $CPU{$arch} }; + + my $out = "Information for $arg ($arch)\n--------\n"; + + if(@{$info{'aliases'}}) { + $out .= 'Aliases: ' . join(' ', @{$info{'aliases'}}) . "\n"; + } + + if(@{$info{'submodels'}}) { + $out .= 'Submodels: ' . join(' ', @{$info{'submodels'}}) . "\n"; + } + + foreach my $k (keys %{$info{'submodel_aliases'}}) { + $out .= "Alias '$k' -> '" . $info{'submodel_aliases'}{$k} . "'\n"; + } + + if(defined($info{'endian'})) { + $out .= 'Default endian: ' . $info{'endian'} . "\n"; + } + + if(defined($info{'unaligned'})) { + $out .= 'Unaligned memory access: ' . $info{'unaligned'} . "\n"; + } + + return $out; + } + + sub add_modules { + my ($config,$mods) = @_; + + foreach my $mod (split(/,/, $mods)) { + # -1 means disabled by user, do not load + $$config{'modules'}{$mod} = 1 unless( + defined($$config{'modules'}{$mod}) && + $$config{'modules'}{$mod} == -1); + } + } + + sub disable_modules { + my ($config,$mods) = @_; + + foreach my $mod (split(/,/, $mods)) { + # -1 means disabled by user, do not load + $$config{'modules'}{$mod} = -1; + } + } + + sub add_module_sets { + my ($config,$sets) = @_; + + foreach my $set (split(/,/, $sets)) { + for my $mod (sort keys %MODULES) { + my %info = %{$MODULES{$mod}}; + + next unless (defined($info{'modset'})); + + for my $s (split(/,/, $info{'modset'})) { + if($s eq $set) { + $$config{'modules'}{$mod} = 1 + unless($$config{'modules'}{$mod} == -1); + } + } + } + } + } + + exit 1 unless GetOptions( + 'prefix=s' => sub { &$save_option(@_); }, + 'exec-prefix=s' => sub { &$save_option(@_); }, + + 'bindir=s' => sub { &$save_option(@_); }, + 'datadir' => sub { &$save_option(@_); }, + 'datarootdir' => sub { &$save_option(@_); }, + 'docdir=s' => sub { &$save_option(@_); }, + 'dvidir' => sub { &$save_option(@_); }, + 'htmldir' => sub { &$save_option(@_); }, + 'includedir' => sub { &$save_option(@_); }, + 'infodir' => sub { &$save_option(@_); }, + 'libdir=s' => sub { &$save_option(@_); }, + 'libexecdir' => sub { &$save_option(@_); }, + 'localedir' => sub { &$save_option(@_); }, + 'localstatedir' => sub { &$save_option(@_); }, + 'mandir' => sub { &$save_option(@_); }, + 'oldincludedir' => sub { &$save_option(@_); }, + 'pdfdir' => sub { &$save_option(@_); }, + 'psdir' => sub { &$save_option(@_); }, + 'sbindir=s' => sub { &$save_option(@_); }, + 'sharedstatedir' => sub { &$save_option(@_); }, + 'sysconfdir' => sub { &$save_option(@_); }, + + 'cc=s' => sub { &$save_option('compiler', $_[1]) }, + 'os=s' => sub { &$save_option(@_) }, + 'cpu=s' => sub { &$save_option(@_) }, + + 'help' => sub { display_help(); }, + 'module-info' => sub { emit_help(module_info()); }, + 'version' => sub { emit_help("$VERSION_STRING\n") }, + 'so-version' => sub { emit_help("$SO_VERSION_STRING\n") }, + + 'with-tr1-implementation=s' => sub { $$config{'tr1'} = $_[1]; }, + + 'quiet' => sub { $$config{'verbose'} = 0; }, + 'trace' => sub { $TRACING = 1; }, + + 'enable-asm' => sub { $$config{'asm_ok'} = 1; }, + 'disable-asm' => sub { $$config{'asm_ok'} = 0; }, + + 'enable-autoconfig' => sub { $$config{'autoconfig'} = 1; }, + 'disable-autoconfig' => sub { $$config{'autoconfig'} = 0; }, + + 'enable-shared' => sub { $$config{'shared'} = 'yes'; }, + 'disable-shared' => sub { $$config{'shared'} = 'no'; }, + + 'enable-debug' => sub { &$save_option('debug', 1); }, + 'disable-debug' => sub { &$save_option('debug', 0); }, + + 'enable-modules:s' => sub { add_modules($config, $_[1]); }, + 'disable-modules:s' => sub { disable_modules($config, $_[1]); }, + + 'with-openssl' => sub { add_modules($config, 'openssl'); }, + 'without-openssl' => sub { disable_modules($config, 'openssl'); }, + 'with-gnump' => sub { add_modules($config, 'gnump'); }, + 'without-gnump' => sub { disable_modules($config, 'gnump'); }, + 'with-bzip2' => sub { add_modules($config, 'bzip2'); }, + 'without-bzip2' => sub { disable_modules($config, 'bzip2'); }, + 'with-zlib' => sub { add_modules($config, 'zlib'); }, + 'without-zlib' => sub { disable_modules($config, 'zlib'); }, + + 'use-module-set=s' => sub { add_module_sets($config, $_[1]); }, + + 'with-build-dir=s' => sub { &$save_option(@_); }, + 'with-endian=s' => sub { &$save_option(@_); }, + 'with-unaligned-mem=s' => sub { &$save_option(@_); }, + 'with-local-config=s' => + sub { &$save_option('local_config', slurp_file($_[1])); }, + + 'modules=s' => sub { add_modules($config, $_[1]); }, + 'show-arch-info=s' => sub { emit_help(arch_info($_[1])); }, + 'make-style=s' => sub { &$save_option(@_); }, + 'dumb-gcc|gcc295x' => sub { $$config{'gcc_bug'} = 1; } + ); + + # All arguments should now be consumed + croak("Unknown option $ARGV[0] (try --help)") unless($#ARGV == -1); +} + +################################################## +# Functions to search the info tables # +################################################## +sub find_arch { + my $name = $_[0]; + + foreach my $arch (keys %CPU) { + my %info = %{$CPU{$arch}}; + + return $arch if($name eq $arch); + + foreach my $alias (@{$info{'aliases'}}) { + return $arch if($name eq $alias); + } + + foreach my $submodel (@{$info{'submodels'}}) { + return $arch if($name eq $submodel); + } + + foreach my $submodel (keys %{$info{'submodel_aliases'}}) { + return $arch if($name eq $submodel); + } + } + return undef; +}; + +sub figure_out_arch { + my ($name) = @_; + + return ('generic', 'generic') if($name eq 'generic'); + + my $submodel_alias = sub { + my ($name,$info) = @_; + + my %info = %{$info}; + + foreach my $submodel (@{$info{'submodels'}}) { + return $submodel if($name eq $submodel); + } + + return '' unless defined $info{'submodel_aliases'}; + my %sm_aliases = %{$info{'submodel_aliases'}}; + + foreach my $alias (keys %sm_aliases) { + my $official = $sm_aliases{$alias}; + return $official if($alias eq $name); + } + return ''; + }; + + my $arch = find_arch($name); + croak("Arch type $name isn't known (try --help)") unless defined $arch; + trace("mapped name '$name' to arch '$arch'"); + + my %archinfo = %{ $CPU{$arch} }; + + my $submodel = &$submodel_alias($name, \%archinfo); + + if($submodel eq '') { + $submodel = $archinfo{'default_submodel'}; + + autoconfig("Using $submodel as default type for family ", + realname($arch)) if($submodel ne $arch); + } + + trace("mapped name '$name' to submodel '$submodel'"); + + croak("Couldn't figure out arch type of $name") + unless defined($arch) and defined($submodel); + + return ($arch,$submodel); +} + +sub os_alias { + my $name = $_[0]; + + foreach my $os (keys %OPERATING_SYSTEM) { + foreach my $alias (@{$OPERATING_SYSTEM{$os}{'aliases'}}) { + if($alias eq $name) { + trace("os_alias($name) -> $os"); + return $os; + } + } + } + + return $name; +} + +sub os_info_for { + my ($os,$what) = @_; + + die unless defined($os); + + croak('os_info_for called with an os of defaults (internal problem)') + if($os eq 'defaults'); + + my $result = ''; + + if(defined($OPERATING_SYSTEM{$os})) { + my %osinfo = %{$OPERATING_SYSTEM{$os}}; + $result = $osinfo{$what}; + } + + if(!defined($result) or $result eq '') { + $result = $OPERATING_SYSTEM{'defaults'}{$what}; + } + + croak("os_info_for: No info for $what on $os") unless defined $result; + + return $result; +} + +sub my_compiler { + my ($config) = @_; + my $cc = $$config{'compiler'}; + + croak('my_compiler called, but no compiler set in config') + unless defined $cc and $cc ne ''; + + croak("unknown compiler $cc") unless defined $COMPILER{$cc}; + + return %{$COMPILER{$cc}}; +} + +sub mach_opt { + my ($config) = @_; + + my %ccinfo = my_compiler($config); + + # Nothing we can do in that case + return '' unless $ccinfo{'mach_opt_flags'}; + + my $submodel = $$config{'submodel'}; + my $arch = $$config{'arch'}; + if(defined($ccinfo{'mach_opt_flags'}{$submodel})) + { + return $ccinfo{'mach_opt_flags'}{$submodel}; + } + elsif(defined($ccinfo{'mach_opt_flags'}{$arch})) { + my $mach_opt_flags = $ccinfo{'mach_opt_flags'}{$arch}; + my $processed_modelname = $submodel; + + my $remove = ''; + if(defined($ccinfo{'mach_opt_re'}) and + defined($ccinfo{'mach_opt_re'}{$arch})) { + $remove = $ccinfo{'mach_opt_re'}{$arch}; + } + + $processed_modelname =~ s/$remove//; + $mach_opt_flags =~ s/SUBMODEL/$processed_modelname/g; + return $mach_opt_flags; + } + return ''; +} + +################################################## +# # +################################################## +sub using_libs { + my ($config) = @_; + + my $os = $$config{'os'}; + my %libs; + + foreach my $mod (sort keys %{$$config{'modules'}}) { + next if ${$$config{'modules'}}{$mod} < 0; + + my %MOD_LIBS = %{ $MODULES{$mod}{'libs'} }; + + foreach my $mod_os (keys %MOD_LIBS) + { + next if($mod_os =~ /^all!$os$/); + next if($mod_os =~ /^all!$os,/); + #next if($mod_os =~ /^all!.*,${os}$/); + next if($mod_os =~ /^all!.*,$os,.*/); + next unless($mod_os eq $os or ($mod_os =~ /^all.*/)); + my @liblist = split(/,/, $MOD_LIBS{$mod_os}); + foreach my $lib (@liblist) { $libs{$lib} = 1; } + } + } + + return sort keys %libs; +} + +sub libs { + my ($prefix,$suffix,@libs) = @_; + my $output = ''; + foreach my $lib (@libs) { + $output .= ' ' if($output ne ''); + $output .= $prefix . $lib . $suffix; + } + return $output; +} + +################################################## +# Path and file manipulation utilities # +################################################## +sub portable_symlink { + my ($from, $to_dir, $to_fname) = @_; + + #trace("portable_symlink($from, $to_dir, $to_fname)"); + + my $can_symlink = 0; + my $can_link = 0; + + unless($^O eq 'MSWin32' or $^O eq 'dos' or $^O eq 'cygwin') { + $can_symlink = eval { symlink("",""); 1 }; + $can_link = eval { link("",""); 1 }; + } + + chdir $to_dir or croak("Can't chdir to $to_dir ($!)"); + + if($can_symlink) { + symlink $from, $to_fname or + croak("Can't symlink $from to $to_fname ($!)"); + } + elsif($can_link) { + link $from, $to_fname or + croak("Can't link $from to $to_fname ($!)"); + } + else { + copy ($from, $to_fname) or + croak("Can't copy $from to $to_fname ($!)"); + } + + my $go_up = File::Spec->splitdir($to_dir); + for(my $j = 0; $j != $go_up; $j++) # return to where we were + { + chdir File::Spec->updir(); + } +} + +sub copy_include_files { + my ($config) = @_; + + my $include_dir = $$config{'build_include_botan'}; + + trace('Copying to ', $include_dir); + + foreach my $file (dir_list($include_dir)) { + my $path = File::Spec->catfile($include_dir, $file); + unlink $path or croak("Could not unlink $path ($!)"); + } + + my $link_up = sub { + my ($dir, $file) = @_; + my $updir = File::Spec->updir(); + portable_symlink(File::Spec->catfile($updir, $updir, $updir, + $dir, $file), + $include_dir, $file); + }; + + my $files = $$config{'includes'}; + + foreach my $file (keys %$files) { + &$link_up($$files{$file}, $file); + } +} + +sub dir_list { + my ($dir) = @_; + opendir(DIR, $dir) or croak("Couldn't read directory '$dir' ($!)"); + + my @listing = grep { !/#/ and -f File::Spec->catfile($dir, $_) and + $_ ne File::Spec->curdir() and + $_ ne File::Spec->updir() } readdir DIR; + + closedir DIR; + return @listing; +} + +sub mkdirs { + my (@dirs) = @_; + + my @created; + foreach my $dir (@dirs) { + next if( -e $dir and -d $dir ); # skip it if it's already there + mkdir($dir, 0777) or + croak("Could not create directory $dir ($!)"); + push @created, $dir; + } + return @created; +} + +sub slurp_file { + my $file = $_[0]; + + return '' if(!defined($file) or $file eq ''); + + croak("'$file': No such file") unless(-e $file); + croak("'$file': Not a regular file") unless(-f $file); + + open FILE, "<$file" or croak("Couldn't read $file ($!)"); + + my $output = ''; + while(<FILE>) { $output .= $_; } + close FILE; + + return $output; +} + +sub which +{ + my $file = $_[0]; + my @paths = split(/:/, $ENV{PATH}); + foreach my $path (@paths) + { + my $file_path = File::Spec->catfile($path, $file); + return $file_path if(-e $file_path and -r $file_path); + } + return ''; +} + +# Return a hash mapping every var in a list to a constant value +sub map_to { + my $var = shift; + return map { $_ => $var } @_; +} + +sub in_array { + my($target, $array) = @_; + return 0 unless defined($array); + foreach (@$array) { return 1 if($_ eq $target); } + return 0; +} + +sub add_to { + my ($to,$from) = @_; + + foreach my $key (keys %$from) { + $$to{$key} = $$from{$key}; + } +} + +################################################## +# # +################################################## +sub find_mp_bits { + my(@modules_list) = @_; + my $mp_bits = 32; # default, good for most systems + + my $seen_mp_module = undef; + + foreach my $modname (sort keys %{$$config{'modules'}}) { + croak("Unknown module $modname") unless defined $MODULES{$modname}; + + next if $$config{'modules'}{$modname} < 0; + + my %modinfo = %{ $MODULES{$modname} }; + if($modinfo{'mp_bits'}) { + if(defined($seen_mp_module) and $modinfo{'mp_bits'} != $mp_bits) { + croak('Inconsistent mp_bits requests from modules ', + $seen_mp_module, ' and ', $modname); + } + + $seen_mp_module = $modname; + $mp_bits = $modinfo{'mp_bits'}; + } + } + return $mp_bits; +} + +################################################## +# # +################################################## +sub realname { + my $arg = $_[0]; + + return $COMPILER{$arg}{'realname'} + if defined $COMPILER{$arg}; + + return $OPERATING_SYSTEM{$arg}{'realname'} + if defined $OPERATING_SYSTEM{$arg}; + + return $CPU{$arg}{'realname'} + if defined $CPU{$arg}; + + return $arg; +} + +################################################## +# # +################################################## + +sub load_module { + my ($config, $modname) = @_; + + #trace("load_module($modname)"); + + croak("Unknown module $modname") unless defined($MODULES{$modname}); + + my %module = %{$MODULES{$modname}}; + + my $works_on = sub { + my ($what, $lst_ref) = @_; + my @lst = @{$lst_ref}; + return 1 if not @lst; # empty list -> no restrictions + return 1 if $what eq 'generic'; # trust the user + return in_array($what, \@lst); + }; + + # Check to see if everything is OK WRT system requirements + my $os = $$config{'os'}; + + croak("Module '$modname' does not run on $os") + unless(&$works_on($os, $module{'os'})); + + my $arch = $$config{'arch'}; + my $sub = $$config{'submodel'}; + + croak("Module '$modname' does not run on $arch/$sub") + unless(&$works_on($arch, $module{'arch'}) or + &$works_on($sub, $module{'arch'})); + + my $cc = $$config{'compiler'}; + + croak("Module '$modname' does not work with $cc") + unless(&$works_on($cc, $module{'cc'})); + + my $handle_files = sub { + my($lst, $func) = @_; + return unless defined($lst); + + foreach (sort @$lst) { + &$func($module{'moddirs'}, $config, $_); + } + }; + + &$handle_files($module{'ignore'}, \&ignore_file); + &$handle_files($module{'add'}, \&add_file); + &$handle_files($module{'replace'}, + sub { ignore_file(@_); add_file(@_); }); + + warning($modname, ': ', $module{'note'}) + if(defined($module{'note'})); +} + +sub load_modules { + my ($config) = @_; + + my @mod_names; + + foreach my $mod (sort keys %{$$config{'modules'}}) { + next unless($$config{'modules'}{$mod} > 0); + + load_module($config, $mod); + + push @mod_names, $mod; + } + + $$config{'mod_list'} = join("\n", @mod_names); + + my $unaligned_ok = 0; + + my $target_os_defines = sub { + my @macro_list; + + my $os = $$config{'os'}; + if($os ne 'generic') { + push @macro_list, '#define BOTAN_TARGET_OS_IS_' . uc $os; + + my @features = @{$OPERATING_SYSTEM{$os}{'target_features'}}; + + for my $feature (@features) { + push @macro_list, '#define BOTAN_TARGET_OS_HAS_' . uc $feature; + } + + } + return join("\n", @macro_list); + }; + + $$config{'target_os_defines'} = &$target_os_defines(); + + my $target_cpu_defines = sub { + my @macro_list; + + my $arch = $$config{'arch'}; + if($arch ne 'generic') { + my %cpu_info = %{$CPU{$arch}}; + my $endian = $cpu_info{'endian'}; + + if(defined($$config{'with_endian'})) { + $endian = $$config{'with_endian'}; + $endian = undef unless($endian eq 'little' || + $endian eq 'big'); + } + elsif(defined($endian)) { + autoconfig("Since arch is $arch, assuming $endian endian mode"); + } + + push @macro_list, "#define BOTAN_TARGET_ARCH_IS_" . (uc $arch); + + my $submodel = $$config{'submodel'}; + if($arch ne $submodel) { + $submodel = uc $submodel; + $submodel =~ tr/-/_/; + $submodel =~ tr/.//; + + push @macro_list, "#define BOTAN_TARGET_CPU_IS_$submodel"; + } + + if(defined($endian)) { + $endian = uc $endian; + push @macro_list, + "#define BOTAN_TARGET_CPU_IS_${endian}_ENDIAN"; + + # See if the user set --with-unaligned-mem + if(defined($$config{'with_unaligned_mem'})) { + my $spec = $$config{'with_unaligned_mem'}; + + if($spec eq 'yes') { + $unaligned_ok = 1; + } + elsif($spec eq 'no') { + $unaligned_ok = 0; + } + else { + warning('Unknown arg to --with-unaligned-mem (' . + $spec . ') will ignore'); + $unaligned_ok = 0; + } + } + # Otherwise, see if the CPU has a default setting + elsif(defined($cpu_info{'unaligned'}) and + $cpu_info{'unaligned'} eq 'ok') + { + autoconfig("Since arch is $arch, " . + 'assuming unaligned memory access is OK'); + $unaligned_ok = 1; + } + } + } + + # variable is always set (one or zero) + push @macro_list, + "#define BOTAN_TARGET_UNALIGNED_LOADSTOR_OK $unaligned_ok"; + return join("\n", @macro_list); + }; + + $$config{'target_cpu_defines'} = &$target_cpu_defines(); + + my $target_compiler_defines = sub { + my @macro_list; + + if(defined($$config{'tr1'})) { + my $tr1 = $$config{'tr1'}; + + if($tr1 eq 'system') { + push @macro_list, '#define BOTAN_USE_STD_TR1'; + } + elsif($tr1 eq 'boost') { + push @macro_list, '#define BOTAN_USE_BOOST_TR1'; + } + elsif($tr1 ne 'none') { + croak("Unknown --with-tr1= option value '$tr1' (try --help)"); + } + } + + return join("\n", @macro_list); + }; + + $$config{'target_compiler_defines'} = &$target_compiler_defines(); + + my $gen_defines = sub { + my @macro_list; + + my %defines; + + foreach my $mod (sort keys %{$$config{'modules'}}) { + next unless $$config{'modules'}{$mod} > 0; + + my $defs = $MODULES{$mod}{'define'}; + next unless $defs; + + push @{$defines{$MODULES{$mod}{'type'}}}, split(/,/, $defs); + } + + foreach my $type (sort keys %defines) { + push @macro_list, "\n/* $type */"; + + for my $macro (@{$defines{$type}}) { + die unless(defined $macro and $macro ne ''); + push @macro_list, "#define BOTAN_HAS_$macro"; + } + } + + return join("\n", @macro_list); + }; + + $$config{'module_defines'} = &$gen_defines(); +} + +################################################## +# # +################################################## +sub file_type { + my ($file) = @_; + + return 'sources' + if($file =~ /\.cpp$/ or $file =~ /\.c$/ or $file =~ /\.S$/); + return 'includes' if($file =~ /\.h$/); + + croak('file_type() - don\'t know what sort of file ', $file, ' is'); +} + +sub add_file { + my ($mod_dir, $config, $file) = @_; + + check_for_file($config, $file, $mod_dir, $mod_dir); + + my $do_add_file = sub { + my ($type) = @_; + + croak("File $file already added from ", $$config{$type}{$file}) + if(defined($$config{$type}{$file})); + + if($file =~ /(.*):(.*)/) { + my @dirs = File::Spec->splitdir($mod_dir); + + $dirs[$#dirs-1] = $1; + + $$config{$type}{$2} = File::Spec->catdir(@dirs); + } + else { + $$config{$type}{$file} = $mod_dir; + } + }; + + &$do_add_file(file_type($file)); +} + +sub ignore_file { + my ($mod_dir, $config, $file) = @_; + check_for_file($config, $file, undef, $mod_dir); + + my $do_ignore_file = sub { + my ($type, $ok_if_from) = @_; + + if(defined ($$config{$type}{$file})) { + + croak("$mod_dir - File $file modified from ", + $$config{$type}{$file}) + if($$config{$type}{$file} ne $ok_if_from); + + delete $$config{$type}{$file}; + } + }; + + &$do_ignore_file(file_type($file)); +} + +sub check_for_file { + my ($config, $file, $added_from, $mod_dir) = @_; + + #trace("check_for_file($file, $added_from, $mod_dir)"); + + my $full_path = sub { + my ($file,$mod_dir) = @_; + + if($file =~ /(.*):(.*)/) { + return File::Spec->catfile($mod_dir, '..', $1, $2); + } else { + return File::Spec->catfile($mod_dir, $file) if(defined($mod_dir)); + + my @typeinfo = file_type($config, $file); + return File::Spec->catfile($typeinfo[1], $file); + } + }; + + $file = &$full_path($file, $added_from); + + croak("Module $mod_dir requires that file $file exist. This error\n ", + 'should never occur; please contact the maintainers with details.') + unless(-e $file); +} + +################################################## +# # +################################################## +sub process_template { + my ($in, $out, $config) = @_; + + trace("process_template: $in -> $out"); + + my $contents = slurp_file($in); + + foreach my $name (keys %$config) { + my $val = $$config{$name}; + + unless(defined $val) { + trace("Undefined variable $name in $in"); + next; + } + + $contents =~ s/\%\{$name\}/$val/g; + } + + if($contents =~ /\%\{([a-z_]*)\}/) { + + sub summarize { + my ($n, $s) = @_; + + $s =~ s/\n/\\n/; # escape newlines + + return $s if(length($s) <= $n); + + return substr($s, 0, 57) . '...'; + } + + foreach my $key (sort keys %$config) { + print with_diagnostic("debug", + "In %config:", $key, " -> ", + summarize(60, $$config{$key})); + } + + croak("Unbound variable '$1' in $in"); + } + + open OUT, ">$out" or croak("Couldn't write $out ($!)"); + print OUT $contents; + close OUT; +} + +################################################## +# # +################################################## +sub read_list { + my ($line, $reader, $marker, $func) = @_; + + if($line =~ m@^<$marker>$@) { + while(1) { + $line = &$reader(); + + die "EOF while searching for $marker" unless $line; + last if($line =~ m@^</$marker>$@); + &$func($line); + } + } +} + +sub list_push { + my ($listref) = @_; + return sub { push @$listref, $_[0]; } +} + +sub match_any_of { + my ($line, $hash, $quoted, @any_of) = @_; + + $quoted = ($quoted eq 'quoted') ? 1 : 0; + + foreach my $what (@any_of) { + $$hash{$what} = $1 if(not $quoted and $line =~ /^$what (.*)/); + $$hash{$what} = $1 if($quoted and $line =~ /^$what \"(.*)\"/); + } +} + +################################################## +# # +################################################## +sub make_reader { + my $filename = $_[0]; + + croak("make_reader(): Arg was undef") if not defined $filename; + + open FILE, "<$filename" or + croak("Couldn't read $filename ($!)"); + + return sub { + my $line = ''; + while(1) { + my $line = <FILE>; + last unless defined($line); + + chomp($line); + $line =~ s/#.*//; + $line =~ s/^\s*//; + $line =~ s/\s*$//; + $line =~ s/\s\s*/ /; + $line =~ s/\t/ /; + return $line if $line ne ''; + } + close FILE; + return undef; + } +} + +################################################## +# # +################################################## +sub read_info_files { + my ($config, $dir, $func) = @_; + + $dir = File::Spec->catdir($$config{'config-dir'}, $dir); + + my %allinfo; + foreach my $file (dir_list($dir)) { + my $fullpath = File::Spec->catfile($dir, $file); + + $file =~ s/.txt//; + + trace("reading $fullpath"); + %{$allinfo{$file}} = &$func($file, $fullpath); + } + + return %allinfo; +} + +sub read_module_files { + my ($config) = @_; + + my %allinfo; + + my @modinfos; + + File::Find::find( + { wanted => sub + { if(-f $_ && /^info\.txt\z/s) { + my $name = $File::Find::name; + push @modinfos, $name; + } + } + }, + $$config{'src-dir'}); + + foreach my $modfile (@modinfos) { + trace("reading $modfile"); + + my ($volume,$dirs,$file) = File::Spec->splitpath($modfile); + + my @dirs = File::Spec->splitdir($dirs); + my $moddir = $dirs[$#dirs-1]; + + trace("module $moddir in $dirs $modfile"); + + %{$allinfo{$moddir}} = get_module_info($dirs, $moddir, $modfile); + } + + return %allinfo; +} + +################################################## +# # +################################################## + +sub get_module_info { + my ($dirs, $name, $modfile) = @_; + my $reader = make_reader($modfile); + + my %info; + + $info{'name'} = $name; + $info{'modinfo'} = $modfile; + $info{'moddirs'} = $dirs; + + # Default module settings + $info{'load_on'} = 'request'; # default unless specified + $info{'uses_tr1'} = 'no'; + $info{'libs'} = {}; + $info{'use'} = 'no'; + + my @dir_arr = File::Spec->splitdir($dirs); + $info{'type'} = $dir_arr[$#dir_arr-2]; # cipher, hash, ... + if($info{'type'} eq 'src') { $info{'type'} = $dir_arr[$#dir_arr-1]; } + + while($_ = &$reader()) { + match_any_of($_, \%info, 'quoted', 'realname', 'note', 'type'); + match_any_of($_, \%info, 'unquoted', 'define', 'mp_bits', + 'modset', 'load_on', 'uses_tr1'); + + read_list($_, $reader, 'arch', list_push(\@{$info{'arch'}})); + read_list($_, $reader, 'cc', list_push(\@{$info{'cc'}})); + read_list($_, $reader, 'os', list_push(\@{$info{'os'}})); + read_list($_, $reader, 'add', list_push(\@{$info{'add'}})); + read_list($_, $reader, 'replace', list_push(\@{$info{'replace'}})); + read_list($_, $reader, 'ignore', list_push(\@{$info{'ignore'}})); + read_list($_, $reader, 'requires', list_push(\@{$info{'requires'}})); + + read_list($_, $reader, 'libs', + sub { + my $line = $_[0]; + $line =~ m/^([\w!,]*) -> ([\w.,-]*)$/; + $info{'libs'}{$1} = $2; + }); + + if(/^require_version /) { + if(/^require_version (\d+)\.(\d+)\.(\d+)$/) { + my $version = "$1.$2.$3"; + my $needed_version = 100*$1 + 10*$2 + $3; + + my $have_version = + 100*$MAJOR_VERSION + 10*$MINOR_VERSION + $PATCH_VERSION; + + if($needed_version > $have_version) { + warning("Module $name needs v$version; disabling"); + return (); + } + } + else { + croak("In module $name, bad version requirement '$_'"); + } + } + } + + return %info; +} + +################################################## +# # +################################################## +sub get_arch_info { + my ($name,$file) = @_; + my $reader = make_reader($file); + + my %info; + $info{'name'} = $name; + + while($_ = &$reader()) { + match_any_of($_, \%info, 'quoted', 'realname'); + match_any_of($_, \%info, 'unquoted', + 'default_submodel', 'endian', 'unaligned'); + + read_list($_, $reader, 'aliases', list_push(\@{$info{'aliases'}})); + read_list($_, $reader, 'submodels', list_push(\@{$info{'submodels'}})); + + read_list($_, $reader, 'submodel_aliases', + sub { + my $line = $_[0]; + $line =~ m/^(\S*) -> (\S*)$/; + $info{'submodel_aliases'}{$1} = $2; + }); + } + return %info; +} + +################################################## +# # +################################################## +sub get_os_info { + my ($name,$file) = @_; + my $reader = make_reader($file); + + my %info; + $info{'name'} = $name; + + while($_ = &$reader()) { + match_any_of($_, \%info, + 'quoted', 'realname', 'ar_command', + 'install_cmd_data', 'install_cmd_exec'); + + match_any_of($_, \%info, 'unquoted', + 'os_type', + 'obj_suffix', + 'so_suffix', + 'static_suffix', + 'install_root', + 'header_dir', + 'lib_dir', 'doc_dir', + 'ar_needs_ranlib'); + + read_list($_, $reader, 'aliases', list_push(\@{$info{'aliases'}})); + + read_list($_, $reader, 'target_features', + list_push(\@{$info{'target_features'}})); + + read_list($_, $reader, 'supports_shared', + list_push(\@{$info{'supports_shared'}})); + } + return %info; +} + +################################################## +# Read a file from misc/config/cc and set the values from +# there into a hash for later reference +################################################## +sub get_cc_info { + my ($name,$file) = @_; + my $reader = make_reader($file); + + my %info; + $info{'name'} = $name; + + while($_ = &$reader()) { + match_any_of($_, \%info, 'quoted', + 'realname', + 'binary_name', + 'compile_option', + 'output_to_option', + 'add_include_dir_option', + 'add_lib_dir_option', + 'add_lib_option', + 'lib_opt_flags', + 'check_opt_flags', + 'dll_import_flags', + 'dll_export_flags', + 'lang_flags', + 'warning_flags', + 'shared_flags', + 'ar_command', + 'debug_flags', + 'no_debug_flags'); + + match_any_of($_, \%info, 'unquoted', + 'makefile_style', + 'compiler_has_tr1'); + + sub quoted_mapping { + my $hashref = $_[0]; + return sub { + my $line = $_[0]; + $line =~ m/^(\S*) -> \"(.*)\"$/; + $$hashref{$1} = $2; + } + } + + read_list($_, $reader, 'mach_abi_linking', + quoted_mapping(\%{$info{'mach_abi_linking'}})); + read_list($_, $reader, 'so_link_flags', + quoted_mapping(\%{$info{'so_link_flags'}})); + + read_list($_, $reader, 'mach_opt', + sub { + my $line = $_[0]; + $line =~ m/^(\S*) -> \"(.*)\" ?(.*)?$/; + $info{'mach_opt_flags'}{$1} = $2; + $info{'mach_opt_re'}{$1} = $3; + }); + + } + return %info; +} + +################################################## +# # +################################################## +sub write_pkg_config { + my ($config) = @_; + + return if($$config{'os'} eq 'generic' or + $$config{'os'} eq 'windows'); + + $$config{'link_to'} = libs('-l', '', 'm', @{$$config{'mod_libs'}}); + + my $botan_config = $$config{'botan_config'}; + + process_template( + File::Spec->catfile($$config{'config-dir'}, 'botan-config.in'), + $botan_config, $config); + chmod 0755, $botan_config; + + process_template( + File::Spec->catfile($$config{'config-dir'}, 'botan.pc.in'), + $$config{'botan_pkgconfig'}, $config); + + delete $$config{'link_to'}; +} + +################################################## +# # +################################################## +sub file_list { + my ($put_in, $from, $to, %files) = @_; + + my $list = ''; + + my $spaces = 16; + + foreach (sort keys %files) { + my $file = $_; + + $file =~ s/$from/$to/ if(defined($from) and defined($to)); + + my $dir = $files{$_}; + $dir = $put_in if defined $put_in; + + if(defined($dir)) { + $list .= File::Spec->catfile ($dir, $file); + } + else { + $list .= $file; + } + + $list .= " \\\n "; + } + + $list =~ s/\\\n +$//; # remove trailing escape + + return $list; +} + +sub build_cmds { + my ($config, $dir, $flags, $files) = @_; + + my $obj_suffix = $$config{'obj_suffix'}; + + my %ccinfo = my_compiler($config); + + my $inc = $ccinfo{'add_include_dir_option'}; + my $from = $ccinfo{'compile_option'}; + my $to = $ccinfo{'output_to_option'}; + + my $inc_dir = $$config{'build_include'}; + + # Probably replace by defaults to -I -c -o + croak('undef value found in build_cmds') + unless defined($inc) and defined($from) and defined($to); + + my $bld_line = "\t\$(CXX) $inc$inc_dir $flags $from\$? $to\$@"; + + my @output_lines; + + foreach (sort keys %$files) { + my $src_file = File::Spec->catfile($$files{$_}, $_); + my $obj_file = File::Spec->catfile($dir, $_); + + $obj_file =~ s/\.cpp$/.$obj_suffix/; + $obj_file =~ s/\.c$/.$obj_suffix/; + $obj_file =~ s/\.S$/.$obj_suffix/; + + push @output_lines, "$obj_file: $src_file\n$bld_line"; + } + + return join("\n\n", @output_lines); +} + +sub determine_config { + my ($config) = @_; + + sub os_ar_command { + return os_info_for(shift, 'ar_command'); + } + + sub append_if { + my($var,$addme,$cond) = @_; + + croak('append_if: reference was undef') unless defined $var; + + if($cond and $addme ne '') { + $$var .= ' ' unless($$var eq '' or $$var =~ / $/); + $$var .= $addme; + } + } + + sub append_ifdef { + my($var,$addme) = @_; + append_if($var, $addme, defined($addme)); + } + + my $empty_if_nil = sub { + my $val = $_[0]; + return $val if defined($val); + return ''; + }; + + my %ccinfo = my_compiler($config); + + my $lang_flags = ''; + append_ifdef(\$lang_flags, $ccinfo{'lang_flags'}); + append_if(\$lang_flags, "-fpermissive", $$config{'gcc_bug'}); + + my $debug = $$config{'debug'}; + + my $lib_opt_flags = ''; + append_ifdef(\$lib_opt_flags, $ccinfo{'lib_opt_flags'}); + append_ifdef(\$lib_opt_flags, $ccinfo{'debug_flags'}) if($debug); + append_ifdef(\$lib_opt_flags, $ccinfo{'no_debug_flags'}) if(!$debug); + + # This is a default that works on most Unix and Unix-like systems + my $ar_command = 'ar crs'; + my $ranlib_command = 'true'; # almost no systems need it anymore + + # See if there are any over-riding methods. We presume if CC is creating + # the static libs, it knows how to create the index itself. + + my $os = $$config{'os'}; + + if($ccinfo{'ar_command'}) { + $ar_command = $ccinfo{'ar_command'}; + } + elsif(os_ar_command($os)) + { + $ar_command = os_ar_command($os); + $ranlib_command = 'ranlib' + if(os_info_for($os, 'ar_needs_ranlib') eq 'yes'); + } + + my $arch = $$config{'arch'}; + + my $abi_opts = ''; + append_ifdef(\$abi_opts, $ccinfo{'mach_abi_linking'}{$arch}); + append_ifdef(\$abi_opts, $ccinfo{'mach_abi_linking'}{$os}); + append_ifdef(\$abi_opts, $ccinfo{'mach_abi_linking'}{'all'}); + $abi_opts = ' ' . $abi_opts if($abi_opts ne ''); + + if($$config{'shared'} eq 'yes' and + (in_array('all', $OPERATING_SYSTEM{$os}{'supports_shared'}) or + in_array($$config{'compiler'}, + $OPERATING_SYSTEM{$os}{'supports_shared'}))) { + + $$config{'shared_flags'} = &$empty_if_nil($ccinfo{'shared_flags'}); + $$config{'so_link'} = &$empty_if_nil($ccinfo{'so_link_flags'}{$os}); + + if($$config{'so_link'} eq '') { + $$config{'so_link'} = + &$empty_if_nil($ccinfo{'so_link_flags'}{'default'}) + } + + if($$config{'shared_flags'} eq '' and $$config{'so_link'} eq '') { + $$config{'shared'} = 'no'; + + warning($$config{'compiler'}, ' has no shared object flags set ', + "for $os; disabling shared"); + } + } + else { + autoconfig("No shared library generated with " . + $$config{'compiler'} . " on " . $$config{'os'}); + + $$config{'shared'} = 'no'; + $$config{'shared_flags'} = ''; + $$config{'so_link'} = ''; + } + + add_to($config, { + 'cc' => $ccinfo{'binary_name'} . $abi_opts, + 'lib_opt' => $lib_opt_flags, + 'check_opt' => &$empty_if_nil($ccinfo{'check_opt_flags'}), + 'mach_opt' => mach_opt($config), + 'lang_flags' => $lang_flags, + 'warn_flags' => &$empty_if_nil($ccinfo{'warning_flags'}), + + 'ar_command' => $ar_command, + 'ranlib_command' => $ranlib_command, + 'static_suffix' => os_info_for($os, 'static_suffix'), + 'so_suffix' => os_info_for($os, 'so_suffix'), + 'obj_suffix' => os_info_for($os, 'obj_suffix'), + + 'dll_export_flags' => $ccinfo{'dll_export_flags'}, + 'dll_import_flags' => $ccinfo{'dll_import_flags'}, + + 'install_cmd_exec' => os_info_for($os, 'install_cmd_exec'), + 'install_cmd_data' => os_info_for($os, 'install_cmd_data'), + }); +} + +sub generate_makefile { + my ($config) = @_; + + my $is_in_doc_dir = + sub { -e File::Spec->catfile($$config{'doc_src_dir'}, $_[0]) }; + + my $docs = file_list(undef, undef, undef, + map_to($$config{'doc_src_dir'}, + grep { &$is_in_doc_dir($_); } @DOCS)); + + $docs .= File::Spec->catfile($$config{'base-dir'}, 'readme.txt'); + + my $includes = file_list(undef, undef, undef, + map_to($$config{'build_include_botan'}, + keys %{$$config{'includes'}})); + + my $lib_objs = file_list($$config{'build_lib'}, '(\.cpp$|\.c$|\.S$)', + '.' . $$config{'obj_suffix'}, + %{$$config{'sources'}}); + + my $check_objs = file_list($$config{'build_check'}, '.cpp', + '.' . $$config{'obj_suffix'}, + %{$$config{'check_src'}}), + + my $lib_build_cmds = build_cmds($config, $$config{'build_lib'}, + '$(LIB_FLAGS)', $$config{'sources'}); + + my $check_build_cmds = build_cmds($config, $$config{'build_check'}, + '$(CHECK_FLAGS)', $$config{'check_src'}); + + add_to($config, { + 'lib_objs' => $lib_objs, + 'check_objs' => $check_objs, + 'lib_build_cmds' => $lib_build_cmds, + 'check_build_cmds' => $check_build_cmds, + + 'doc_files' => $docs, + 'include_files' => $includes + }); + + my $template_dir = File::Spec->catdir($$config{'config-dir'}, 'makefile'); + my $template = undef; + + my $make_style = $$config{'make_style'}; + + if($make_style eq 'unix') { + $template = File::Spec->catfile($template_dir, 'unix.in'); + + $template = File::Spec->catfile($template_dir, 'unix_shr.in') + if($$config{'shared'} eq 'yes'); + + add_to($config, { + 'link_to' => libs('-l', '', 'm', @{$$config{'mod_libs'}}), + }); + } + elsif($make_style eq 'nmake') { + $template = File::Spec->catfile($template_dir, 'nmake.in'); + + add_to($config, { + 'shared' => 'no', + 'link_to' => libs('', '', '', @{$$config{'mod_libs'}}), + }); + } + + croak("Don't know about makefile format '$make_style'") + unless defined $template; + + trace("'$make_style' -> '$template'"); + + process_template($template, $$config{'makefile'}, $config); + + autoconfig("Wrote ${make_style}-style makefile in $$config{'makefile'}"); +} + +################################################## +# Configuration Guessing # +################################################## +sub guess_cpu_from_this +{ + my $cpuinfo = lc $_[0]; + + $cpuinfo =~ s/\(r\)//g; + $cpuinfo =~ s/\(tm\)//g; + $cpuinfo =~ s/ //g; + + trace("guess_cpu_from_this($cpuinfo)"); + + # The 32-bit SPARC stuff is impossible to match to arch type easily, and + # anyway the uname stuff will pick up that it's a SPARC so it doesn't + # matter. If it's an Ultra, assume a 32-bit userspace, no 64-bit code + # possible; that's the most common setup right now anyway + return 'sparc32-v9' if($cpuinfo =~ /ultrasparc/); + + # Should probably do this once and cache it + my @names; + my %all_alias; + + foreach my $arch (keys %CPU) { + my %info = %{$CPU{$arch}}; + + foreach my $submodel (@{$info{'submodels'}}) { + push @names, $submodel; + } + + if(defined($info{'submodel_aliases'})) { + my %submodel_aliases = %{$info{'submodel_aliases'}}; + foreach my $sm_alias (keys %submodel_aliases) { + push @names, $sm_alias; + $all_alias{$sm_alias} = $submodel_aliases{$sm_alias}; + } + } + } + + @names = sort { length($b) <=> length($a) } @names; + + foreach my $name (@names) { + if($cpuinfo =~ $name) { + trace("Matched '$cpuinfo' against '$name'"); + + return $all_alias{$name} if defined($all_alias{$name}); + + return $name; + } + } + + trace("Couldn't match $cpuinfo against any submodels"); + + # No match? Try arch names. Reset @names + @names = (); + + foreach my $arch (keys %CPU) { + my %info = %{$CPU{$arch}}; + + push @names, $info{'name'}; + + foreach my $alias (@{$info{'aliases'}}) { + push @names, $alias; + } + } + + @names = sort { length($b) <=> length($a) } @names; + + foreach my $name (@names) { + if($cpuinfo =~ $name) { + trace("Matched '$cpuinfo' against '$name'"); + return $name; + } + } + + return ''; +} + +# Do some WAGing and see if we can figure out what system we are. Think about +# this as a really moronic config.guess +sub guess_compiler +{ + my @CCS = ('gcc', 'msvc', 'icc', 'compaq', 'kai'); + + # First try the CC enviornmental variable, if it's set + if(defined($ENV{CC})) + { + my @new_CCS = ($ENV{CC}); + foreach my $cc (@CCS) { push @new_CCS, $cc; } + @CCS = @new_CCS; + } + + foreach (@CCS) + { + my $bin_name = $COMPILER{$_}{'binary_name'}; + if(which($bin_name) ne '') { + autoconfig("Guessing to use $_ as the compiler " . + "(use --cc to set)"); + return $_; + } + } + + croak( + "Can't find a usable C++ compiler, is PATH right?\n" . + "You might need to run with the --cc option (try $0 --help)\n"); +} + +sub guess_os +{ + sub recognize_os + { + my $os = os_alias($_[0]); + if(defined($OPERATING_SYSTEM{$os})) { + autoconfig("Guessing operating system is $os (use --os to set)"); + return $os; + } + return undef; + } + + my $guess = recognize_os($^O); + return $guess if $guess; + + trace("Can't guess os from $^O"); + + my $uname = $$config{'uname'}; + + if($uname ne '') { + $guess = recognize_os($uname); + return $guess if $guess; + trace("Can't guess os from $uname"); + } + + warning("Unknown OS ('$^O', '$uname'), falling back to generic code"); + return 'generic'; +} + +sub guess_cpu +{ + # If we have /proc/cpuinfo, try to get nice specific information about + # what kind of CPU we're running on. + my $cpuinfo = '/proc/cpuinfo'; + + if(defined($ENV{'CPUINFO'})) { + my $cpuinfo_env = $ENV{'CPUINFO'}; + + if(-e $cpuinfo_env and -r $cpuinfo_env) { + autoconfig("Will use $cpuinfo_env as /proc/cpuinfo"); + $cpuinfo = $cpuinfo_env; + } else { + warn("Could not read from ENV /proc/cpuinfo ($cpuinfo_env)"); + } + } + + if(-e $cpuinfo and -r $cpuinfo) + { + open CPUINFO, $cpuinfo or die "Could not read $cpuinfo\n"; + + while(<CPUINFO>) { + + chomp; + $_ =~ s/\t/ /g; + $_ =~ s/ +/ /g; + + if($_ =~ /^cpu +: (.*)/ or + $_ =~ /^model name +: (.*)/) + { + my $cpu = guess_cpu_from_this($1); + if($cpu ne '') { + autoconfig("Guessing CPU using $cpuinfo line '$_'"); + autoconfig("Guessing CPU is a $cpu (use --cpu to set)"); + return $cpu; + } + } + } + + autoconfig("*** Could not figure out CPU based on $cpuinfo"); + autoconfig("*** Please mail contents to lloyd\@randombit.net"); + } + + sub known_arch { + my ($name) = @_; + + foreach my $arch (keys %CPU) { + my %info = %{$CPU{$arch}}; + + return 1 if $name eq $info{'name'}; + foreach my $submodel (@{$info{'submodels'}}) { + return 1 if $name eq $submodel; + } + + foreach my $alias (@{$info{'aliases'}}) { + return 1 if $name eq $alias; + } + + if(defined($info{'submodel_aliases'})) { + my %submodel_aliases = %{$info{'submodel_aliases'}}; + foreach my $sm_alias (keys %submodel_aliases) { + return 1 if $name eq $sm_alias; + } + } + } + + my $guess = guess_cpu_from_this($name); + + return 0 if($guess eq $name or $guess eq ''); + + return known_arch($guess); + } + + my $uname = $$config{'uname'}; + if($uname ne '') { + my $cpu = guess_cpu_from_this($uname); + + if($cpu ne '') + { + autoconfig("Guessing CPU using uname output '$uname'"); + autoconfig("Guessing CPU is a $cpu (use --cpu to set)"); + + return $cpu if known_arch($cpu); + } + } + + my $config_archname = $Config{'archname'}; + my $cpu = guess_cpu_from_this($config_archname); + + if($cpu ne '') + { + autoconfig("Guessing CPU using Config{archname} '$config_archname'"); + autoconfig("Guessing CPU is a $cpu (use --cpu to set)"); + + return $cpu if known_arch($cpu); + } + + warning("Could not determine CPU type (try --cpu option)"); + return 'generic'; +} |