diff options
Diffstat (limited to 'doc/examples')
-rw-r--r-- | doc/examples/Makefile | 124 | ||||
-rw-r--r-- | doc/examples/asn1.cpp | 294 | ||||
-rw-r--r-- | doc/examples/base.cpp | 30 | ||||
-rw-r--r-- | doc/examples/base64.cpp | 81 | ||||
-rw-r--r-- | doc/examples/bzip.cpp | 102 | ||||
-rw-r--r-- | doc/examples/ca.cpp | 65 | ||||
-rw-r--r-- | doc/examples/decrypt.cpp | 158 | ||||
-rw-r--r-- | doc/examples/dh.cpp | 54 | ||||
-rw-r--r-- | doc/examples/dsa_kgen.cpp | 57 | ||||
-rw-r--r-- | doc/examples/dsa_sign.cpp | 79 | ||||
-rw-r--r-- | doc/examples/dsa_ver.cpp | 94 | ||||
-rw-r--r-- | doc/examples/encrypt.cpp | 175 | ||||
-rw-r--r-- | doc/examples/fips140.cpp | 59 | ||||
-rw-r--r-- | doc/examples/hash.cpp | 64 | ||||
-rw-r--r-- | doc/examples/hash_fd.cpp | 70 | ||||
-rw-r--r-- | doc/examples/hasher.cpp | 58 | ||||
-rw-r--r-- | doc/examples/hasher2.cpp | 72 | ||||
-rw-r--r-- | doc/examples/pkcs10.cpp | 68 | ||||
-rw-r--r-- | doc/examples/readme.txt | 77 | ||||
-rw-r--r-- | doc/examples/rsa_dec.cpp | 122 | ||||
-rw-r--r-- | doc/examples/rsa_enc.cpp | 149 | ||||
-rw-r--r-- | doc/examples/rsa_kgen.cpp | 55 | ||||
-rw-r--r-- | doc/examples/self_sig.cpp | 76 | ||||
-rw-r--r-- | doc/examples/stack.cpp | 86 | ||||
-rw-r--r-- | doc/examples/x509info.cpp | 142 | ||||
-rw-r--r-- | doc/examples/xor_ciph.cpp | 95 |
26 files changed, 2506 insertions, 0 deletions
diff --git a/doc/examples/Makefile b/doc/examples/Makefile new file mode 100644 index 000000000..351d802da --- /dev/null +++ b/doc/examples/Makefile @@ -0,0 +1,124 @@ +# Assumes Botan was compiled with GCC + +BOTAN_DIR = ../.. + +CXX = g++ +WARNINGS = -ansi -W -Wall +#CXX = icc +#WARNINGS = -w1 + +INCLUDES = `$(BOTAN_DIR)/botan-config --cflags` +LIBS = `$(BOTAN_DIR)/botan-config --libs` +FLAGS = $(INCLUDES) $(WARNINGS) -I$(BOTAN_DIR)/build/include -L$(BOTAN_DIR) + +X509_EX = ca pkcs10 self_sig x509info asn1 +RSA_EX = rsa_kgen rsa_enc rsa_dec +DSA_EX = dsa_kgen dsa_sign dsa_ver +DH_EX = dh +HASH_EX = hash hash_fd hasher hasher2 stack +MISC_EX = base base64 bzip encrypt decrypt fips140 xor_ciph + +PROGS = $(X509_EX) $(RSA_EX) $(DSA_EX) $(DH_EX) $(HASH_EX) $(MISC_EX) + +STRIP = true + +all: $(PROGS) + +clean: + @rm -f $(PROGS) + +asn1: asn1.cpp + $(CXX) $(FLAGS) $? $(LIBS) -o $@ + @$(STRIP) $@ + +base: base.cpp + $(CXX) $(FLAGS) $? $(LIBS) -o $@ + @$(STRIP) $@ + +base64: base64.cpp + $(CXX) $(FLAGS) $? $(LIBS) -o $@ + @$(STRIP) $@ + +bzip: bzip.cpp + $(CXX) $(FLAGS) $? $(LIBS) -o $@ + @$(STRIP) $@ + +ca: ca.cpp + $(CXX) $(FLAGS) $? $(LIBS) -o $@ + @$(STRIP) $@ + +decrypt: decrypt.cpp + $(CXX) $(FLAGS) $? $(LIBS) -o $@ + @$(STRIP) $@ + +dh: dh.cpp + $(CXX) $(FLAGS) $? $(LIBS) -o $@ + @$(STRIP) $@ + +dsa_kgen: dsa_kgen.cpp + $(CXX) $(FLAGS) $? $(LIBS) -o $@ + @$(STRIP) $@ + +dsa_sign: dsa_sign.cpp + $(CXX) $(FLAGS) $? $(LIBS) -o $@ + @$(STRIP) $@ + +dsa_ver: dsa_ver.cpp + $(CXX) $(FLAGS) $? $(LIBS) -o $@ + @$(STRIP) $@ + +encrypt: encrypt.cpp + $(CXX) $(FLAGS) $? $(LIBS) -o $@ + @$(STRIP) $@ + +fips140: fips140.cpp + $(CXX) $(FLAGS) $? $(LIBS) -o $@ + @$(STRIP) $@ + +hash: hash.cpp + $(CXX) $(FLAGS) $? $(LIBS) -o $@ + @$(STRIP) $@ + +hash_fd: hash_fd.cpp + $(CXX) $(FLAGS) $? $(LIBS) -o $@ + @$(STRIP) $@ + +hasher: hasher.cpp + $(CXX) $(FLAGS) $? $(LIBS) -o $@ + @$(STRIP) $@ + +hasher2: hasher2.cpp + $(CXX) $(FLAGS) $? $(LIBS) -o $@ + @$(STRIP) $@ + +pkcs10: pkcs10.cpp + $(CXX) $(FLAGS) $? $(LIBS) -o $@ + @$(STRIP) $@ + +rsa_dec: rsa_dec.cpp + $(CXX) $(FLAGS) $? $(LIBS) -o $@ + @$(STRIP) $@ + +rsa_enc: rsa_enc.cpp + $(CXX) $(FLAGS) $? $(LIBS) -o $@ + @$(STRIP) $@ + +rsa_kgen: rsa_kgen.cpp + $(CXX) $(FLAGS) $? $(LIBS) -o $@ + @$(STRIP) $@ + +self_sig: self_sig.cpp + $(CXX) $(FLAGS) $? $(LIBS) -o $@ + @$(STRIP) $@ + +stack: stack.cpp + $(CXX) $(FLAGS) $? $(LIBS) -o $@ + @$(STRIP) $@ + +x509info: x509info.cpp + $(CXX) $(FLAGS) $? $(LIBS) -o $@ + @$(STRIP) $@ + +xor_ciph: xor_ciph.cpp + $(CXX) $(FLAGS) $? $(LIBS) -o $@ + @$(STRIP) $@ diff --git a/doc/examples/asn1.cpp b/doc/examples/asn1.cpp new file mode 100644 index 000000000..91fb995e1 --- /dev/null +++ b/doc/examples/asn1.cpp @@ -0,0 +1,294 @@ +/* + A simple ASN.1 parser, similiar to 'dumpasn1' or 'openssl asn1parse', though + without some of the bells and whistles of those. Primarily used for testing + the BER decoder. The output format is modeled loosely on 'asn1parse -i' + + The output is actually less precise than the other decoders named, because + the underlying BER_Decoder hides quite a bit from userspace, such as the use + of indefinite length encodings (and the EOC markers). At some point it will + also hide the constructed string types from the user, but right now you'll + seem them as-is. + + Written by Jack Lloyd, November 9-10, 2003 + - Nov 22: Updated to new BER_Object format (tag -> class_tag/type_tag) + - Nov 25: Much improved BIT STRING output + Can deal with non-constructed taggings + Can produce UTF-8 output + + This file is in the public domain. +*/ + +/*******************************************************************/ + +// Set this if your terminal understands UTF-8; otherwise output is in Latin-1 +#define UTF8_TERMINAL 1 + +/* + What level the outermost layer of stuff is at. Probably 0 or 1; asn1parse + uses 0 as the outermost, while 1 makes more sense to me. 2+ doesn't make + much sense at all. +*/ +#define INITIAL_LEVEL 0 + +/*******************************************************************/ + +#include <botan/botan.h> +#include <botan/ber_dec.h> +#include <botan/asn1_obj.h> +#include <botan/oids.h> +#include <botan/pem.h> +#include <botan/charset.h> +using namespace Botan; + +#include <stdio.h> +#include <ctype.h> + +void decode(BER_Decoder&, u32bit); +void emit(const std::string&, u32bit, u32bit, const std::string& = ""); +std::string type_name(ASN1_Tag); + +int main(int argc, char* argv[]) + { + if(argc != 2) + { + printf("Usage: %s <file>\n", argv[0]); + return 1; + } + + try { + LibraryInitializer init; + + DataSource_Stream in(argv[1]); + + if(!PEM_Code::matches(in)) + { + BER_Decoder decoder(in); + decode(decoder, INITIAL_LEVEL); + } + else + { + std::string label; // ignored + BER_Decoder decoder(PEM_Code::decode(in, label)); + decode(decoder, INITIAL_LEVEL); + } + + } + catch(std::exception& e) + { + printf("%s\n", e.what()); + return 1; + } + return 0; + } + +void decode(BER_Decoder& decoder, u32bit level) + { + BER_Object obj = decoder.get_next_object(); + + while(obj.type_tag != NO_OBJECT) + { + const ASN1_Tag type_tag = obj.type_tag; + const ASN1_Tag class_tag = obj.class_tag; + const u32bit length = obj.value.size(); + + /* hack to insert the tag+length back in front of the stuff now + that we've gotten the type info */ + DER_Encoder encoder; + encoder.add_object(type_tag, class_tag, obj.value, obj.value.size()); + SecureVector<byte> bits = encoder.get_contents(); + + BER_Decoder data(bits); + + if(class_tag & CONSTRUCTED) + { + BER_Decoder cons_info(obj.value); + if(type_tag == SEQUENCE) + { + emit("SEQUENCE", level, length); + decode(cons_info, level+1); + } + else if(type_tag == SET) + { + emit("SET", level, length); + decode(cons_info, level+1); + } + else + { + std::string name; + + if((class_tag & APPLICATION) || (class_tag & CONTEXT_SPECIFIC) || + (class_tag & PRIVATE)) + name = "cons [" + to_string(type_tag) + "]"; + else + name = type_name(type_tag) + " (cons)"; + + emit(name, level, length); + decode(cons_info, level+1); + } + } + else if(class_tag == APPLICATION || class_tag == CONTEXT_SPECIFIC || + class_tag == PRIVATE) + { + bool not_text = false; + + for(u32bit j = 0; j != bits.size(); j++) + if(!isgraph(bits[j]) && !isspace(bits[j])) + not_text = true; + + Pipe pipe(((not_text) ? new Hex_Encoder : 0)); + pipe.process_msg(bits); + emit("[" + to_string(type_tag) + "]", level, length, + pipe.read_all_as_string()); + } + else if(type_tag == OBJECT_ID) + { + OID oid; + BER::decode(data, oid); + emit(type_name(type_tag), level, length, OIDS::lookup(oid)); + } + else if(type_tag == INTEGER) + { + BigInt number; + BER::decode(data, number); + + SecureVector<byte> rep; + + /* If it's small, it's probably a number, not a hash */ + if(number.bits() <= 16) + rep = BigInt::encode(number, BigInt::Decimal); + else + rep = BigInt::encode(number, BigInt::Hexadecimal); + + std::string str; + for(u32bit j = 0; j != rep.size(); j++) + str += (char)rep[j]; + + emit(type_name(type_tag), level, length, str); + } + else if(type_tag == BOOLEAN) + { + bool boolean; + BER::decode(data, boolean); + emit(type_name(type_tag), + level, length, (boolean ? "true" : "false")); + } + else if(type_tag == NULL_TAG) + { + emit(type_name(type_tag), level, length); + } + else if(type_tag == OCTET_STRING) + { + SecureVector<byte> bits; + BER::decode(data, bits, type_tag); + bool not_text = false; + + for(u32bit j = 0; j != bits.size(); j++) + if(!isgraph(bits[j]) && !isspace(bits[j])) + not_text = true; + + Pipe pipe(((not_text) ? new Hex_Encoder : 0)); + pipe.process_msg(bits); + emit(type_name(type_tag), level, length, pipe.read_all_as_string()); + } + else if(type_tag == BIT_STRING) + { + SecureVector<byte> bits; + BER::decode(data, bits, type_tag); + + std::vector<bool> bit_set; + + for(u32bit j = 0; j != bits.size(); j++) + for(u32bit k = 0; k != 8; k++) + bit_set.push_back((bool)((bits[bits.size()-j-1] >> (7-k)) & 1)); + + std::string bit_str; + for(u32bit j = 0; j != bit_set.size(); j++) + { + bool the_bit = bit_set[bit_set.size()-j-1]; + + if(!the_bit && bit_str.size() == 0) + continue; + bit_str += (the_bit ? "1" : "0"); + } + + emit(type_name(type_tag), level, length, bit_str); + } + else if(type_tag == PRINTABLE_STRING || + type_tag == NUMERIC_STRING || + type_tag == IA5_STRING || + type_tag == T61_STRING || + type_tag == VISIBLE_STRING || + type_tag == UTF8_STRING || + type_tag == BMP_STRING) + { + ASN1_String str; + BER::decode(data, str); + if(UTF8_TERMINAL) + emit(type_name(type_tag), level, length, iso2utf(str.iso_8859())); + else + emit(type_name(type_tag), level, length, str.iso_8859()); + } + else if(type_tag == UTC_TIME || type_tag == GENERALIZED_TIME) + { + X509_Time time; + BER::decode(data, time); + emit(type_name(type_tag), level, length, time.readable_string()); + } + else + fprintf(stderr, "Unknown tag: class=%02X, type=%02X\n", + class_tag, type_tag); + + obj = decoder.get_next_object(); + } + } + +void emit(const std::string& type, u32bit level, u32bit length, + const std::string& value) + { + const u32bit LIMIT = 128; + const u32bit BIN_LIMIT = 64; + + int written = 0; + written += printf(" d=%2d, l=%4d: ", level, length); + for(u32bit j = INITIAL_LEVEL; j != level; j++) + written += printf(" "); + written += printf("%s ", type.c_str()); + + bool should_skip = false; + if(value.length() > LIMIT) should_skip = true; + if((type == "OCTET STRING" || type == "BIT STRING") && + value.length() > BIN_LIMIT) + should_skip = true; + + if(value != "" && !should_skip) + { + if(written % 2 == 0) printf(" "); + while(written < 50) written += printf(" "); + printf(":%s\n", value.c_str()); + } + else + printf("\n"); + } + +std::string type_name(ASN1_Tag type) + { + if(type == PRINTABLE_STRING) return "PRINTABLE STRING"; + if(type == NUMERIC_STRING) return "NUMERIC STRING"; + if(type == IA5_STRING) return "IA5 STRING"; + if(type == T61_STRING) return "T61 STRING"; + if(type == UTF8_STRING) return "UTF8 STRING"; + if(type == VISIBLE_STRING) return "VISIBLE STRING"; + if(type == BMP_STRING) return "BMP STRING"; + + if(type == UTC_TIME) return "UTC TIME"; + if(type == GENERALIZED_TIME) return "GENERALIZED TIME"; + + if(type == OCTET_STRING) return "OCTET STRING"; + if(type == BIT_STRING) return "BIT STRING"; + + if(type == INTEGER) return "INTEGER"; + if(type == NULL_TAG) return "NULL"; + if(type == OBJECT_ID) return "OBJECT"; + if(type == BOOLEAN) return "BOOLEAN"; + return "(UNKNOWN)"; + } diff --git a/doc/examples/base.cpp b/doc/examples/base.cpp new file mode 100644 index 000000000..5a1328ccf --- /dev/null +++ b/doc/examples/base.cpp @@ -0,0 +1,30 @@ +/* + A simple template for Botan applications, showing startup, etc +*/ +#include <botan/botan.h> +using namespace Botan; + +/* This is how you can do compile-time version checking */ +/* +#if BOTAN_VERSION_CODE < BOTAN_VERSION_CODE_FOR(1,3,9) + #error Your Botan installation is too old; upgrade to 1.3.9 or later +#endif +*/ + +#include <iostream> + +int main() + { + try { + /* Put it inside the try block so exceptions at startup/shutdown will + get caught. + */ + LibraryInitializer init; + } + catch(std::exception& e) + { + std::cout << e.what() << std::endl; + return 1; + } + return 0; + } diff --git a/doc/examples/base64.cpp b/doc/examples/base64.cpp new file mode 100644 index 000000000..c1260b8f4 --- /dev/null +++ b/doc/examples/base64.cpp @@ -0,0 +1,81 @@ +/* +An Botan example application which emulates a poorly written version of +"uuencode -m" + +Written by Jack Lloyd ([email protected]), in maybe an hour scattered +over 2000/2001 + +This file is in the public domain +*/ +#include <fstream> +#include <iostream> +#include <string> +#include <vector> +#include <cstring> +#include <botan/botan.h> + +int main(int argc, char* argv[]) + { + if(argc < 2) + { + std::cout << "Usage: " << argv[0] << " [-w] [-c n] [-e|-d] files...\n" + " -e : Encode input to base64 strings (default) \n" + " -d : Decode base64 input\n" + " -w : Wrap lines\n" + " -c n: Wrap lines at column n, default 78\n"; + return 1; + } + + Botan::LibraryInitializer init; + + int column = 78; + bool wrap = false; + bool encoding = true; + std::vector<std::string> files; + + for(int j = 1; argv[j] != 0; j++) + { + std::string this_arg = argv[j]; + + if(this_arg == "-w") + wrap = true; + else if(this_arg == "-e"); + else if(this_arg == "-d") + encoding = false; + else if(this_arg == "-c") + { + if(argv[j+1]) + { column = atoi(argv[j+1]); j++; } + else + { + std::cout << "No argument for -c option" << std::endl; + return 1; + } + } + else files.push_back(argv[j]); + } + + for(unsigned int j = 0; j != files.size(); j++) + { + std::istream* stream; + if(files[j] == "-") stream = &std::cin; + else stream = new std::ifstream(files[j].c_str()); + + if(!*stream) + { + std::cout << "ERROR, couldn't open " << files[j] << std::endl; + continue; + } + + Botan::Pipe pipe((encoding) ? + ((Botan::Filter*)new Botan::Base64_Encoder(wrap, column)) : + ((Botan::Filter*)new Botan::Base64_Decoder)); + pipe.start_msg(); + *stream >> pipe; + pipe.end_msg(); + pipe.set_default_msg(j); + std::cout << pipe; + if(files[j] != "-") delete stream; + } + return 0; + } diff --git a/doc/examples/bzip.cpp b/doc/examples/bzip.cpp new file mode 100644 index 000000000..46ac8abce --- /dev/null +++ b/doc/examples/bzip.cpp @@ -0,0 +1,102 @@ +/* +An Botan example application which emulates a poorly written version of bzip2 + +Written by Jack Lloyd ([email protected]), Jun 9, 2001 + +This file is in the public domain +*/ +#include <string> +#include <cstring> +#include <vector> +#include <fstream> +#include <iostream> +#include <botan/botan.h> + +#if defined(BOTAN_EXT_COMPRESSOR_BZIP2) + #include <botan/bzip2.h> +#else + #error "You didn't compile the bzip module into Botan" +#endif + +const std::string SUFFIX = ".bz2"; + +int main(int argc, char* argv[]) + { + if(argc < 2) + { + std::cout << "Usage: " << argv[0] + << " [-s] [-d] [-1...9] <filenames>" << std::endl; + return 1; + } + + Botan::LibraryInitializer init; + + std::vector<std::string> files; + bool decompress = false, small = false; + int level = 9; + + for(int j = 1; argv[j] != 0; j++) + { + if(std::strcmp(argv[j], "-d") == 0) { decompress = true; continue; } + if(std::strcmp(argv[j], "-s") == 0) { small = true; continue; } + if(std::strcmp(argv[j], "-1") == 0) { level = 1; continue; } + if(std::strcmp(argv[j], "-2") == 0) { level = 2; continue; } + if(std::strcmp(argv[j], "-3") == 0) { level = 3; continue; } + if(std::strcmp(argv[j], "-4") == 0) { level = 4; continue; } + if(std::strcmp(argv[j], "-5") == 0) { level = 5; continue; } + if(std::strcmp(argv[j], "-6") == 0) { level = 6; continue; } + if(std::strcmp(argv[j], "-7") == 0) { level = 7; continue; } + if(std::strcmp(argv[j], "-8") == 0) { level = 8; continue; } + if(std::strcmp(argv[j], "-9") == 0) { level = 9; continue; } + files.push_back(argv[j]); + } + + try { + + Botan::Filter* bzip; + if(decompress) + bzip = new Botan::Bzip_Decompression(small); + else + bzip = new Botan::Bzip_Compression(level); + + Botan::Pipe pipe(bzip); + + for(unsigned int j = 0; j != files.size(); j++) + { + std::string infile = files[j], outfile = files[j]; + if(!decompress) + outfile = outfile += SUFFIX; + else + outfile = outfile.replace(outfile.find(SUFFIX), + SUFFIX.length(), ""); + + std::ifstream in(infile.c_str()); + std::ofstream out(outfile.c_str()); + if(!in) + { + std::cout << "ERROR: could not read " << infile << std::endl; + continue; + } + if(!out) + { + std::cout << "ERROR: could not write " << outfile << std::endl; + continue; + } + + pipe.start_msg(); + in >> pipe; + pipe.end_msg(); + pipe.set_default_msg(j); + out << pipe; + + in.close(); + out.close(); + } + } + catch(std::exception& e) + { + std::cout << "Exception caught: " << e.what() << std::endl; + return 1; + } + return 0; + } diff --git a/doc/examples/ca.cpp b/doc/examples/ca.cpp new file mode 100644 index 000000000..fbc637bbc --- /dev/null +++ b/doc/examples/ca.cpp @@ -0,0 +1,65 @@ +/* + Implement the functionality of a simple CA: read in a CA certificate, + the associated private key, and a PKCS #10 certificate request. Sign the + request and print out the new certificate. + + File names are hardcoded for simplicity. + cacert.pem: The CA's certificate (perhaps created by self_sig) + caprivate.pem: The CA's private key + req.pem: The user's PKCS #10 certificate request + + Written by Jack Lloyd, May 19, 2003 + + This file is in the public domain. +*/ + +#include <botan/botan.h> +#include <botan/x509_ca.h> +using namespace Botan; + +#include <iostream> + +#define DOUCH_BAG CESSATION_OF_OPERATION + +int main(int argc, char* argv[]) + { + if(argc != 2) + { + std::cout << "Usage: " << argv[0] << " passphrase" << std::endl; + return 1; + } + + try { + LibraryInitializer init; + + // set up our CA + X509_Certificate ca_cert("cacert.pem"); + std::auto_ptr<PKCS8_PrivateKey> privkey( + PKCS8::load_key("caprivate.pem", argv[1]) + ); + X509_CA ca(ca_cert, *privkey); + + // got a request + PKCS10_Request req("req.pem"); + + // presumably attempt to verify the req for sanity/accuracy here, but + // as Verisign, etc have shown, that's not a must. :) + + // now sign it + X509_Certificate new_cert = ca.sign_request(req); + + // send the new cert back to the requestor + std::cout << new_cert.PEM_encode(); + + std::vector<CRL_Entry> revoked_certs; + revoked_certs.push_back(CRL_Entry(new_cert, DOUCH_BAG)); + X509_CRL crl = ca.update_crl(ca.new_crl(), revoked_certs); + std::cout << crl.PEM_encode(); + } + catch(std::exception& e) + { + std::cout << e.what() << std::endl; + return 1; + } + return 0; + } diff --git a/doc/examples/decrypt.cpp b/doc/examples/decrypt.cpp new file mode 100644 index 000000000..84490cb1b --- /dev/null +++ b/doc/examples/decrypt.cpp @@ -0,0 +1,158 @@ +/* +Decrypt files encrypted with the 'encrypt' example application. + +I'm being lazy and writing the output to stdout rather than stripping off the +".enc" suffix and writing it there. So all diagnostics go to stderr so there is +no confusion. + +Written by Jack Lloyd ([email protected]) on August 5, 2002 + +This file is in the public domain +*/ +#include <fstream> +#include <iostream> +#include <string> +#include <vector> +#include <cstring> + +#include <botan/botan.h> + +#if defined(BOTAN_EXT_COMPRESSOR_ZLIB) + #include <botan/zlib.h> +#else + #error "You didn't compile the zlib module into Botan" +#endif + +using namespace Botan; + +SecureVector<byte> b64_decode(const std::string&); + +int main(int argc, char* argv[]) + { + if(argc < 2) + { + std::cout << "Usage: " << argv[0] << " [-p passphrase] file\n" + << " -p : Use this passphrase to decrypt\n"; + return 1; + } + + std::string filename, passphrase; + + for(int j = 1; argv[j] != 0; j++) + { + if(std::strcmp(argv[j], "-p") == 0) + { + if(argv[j+1]) + { + passphrase = argv[j+1]; + j++; + } + else + { + std::cout << "No argument for -p option" << std::endl; + return 1; + } + } + else + { + if(filename != "") + { + std::cout << "You can only specify one file at a time\n"; + return 1; + } + filename = argv[j]; + } + } + + if(passphrase == "") + { + std::cout << "You have to specify a passphrase!" << std::endl; + return 1; + } + + std::ifstream in(filename.c_str()); + if(!in) + { + std::cout << "ERROR: couldn't open " << filename << std::endl; + return 1; + } + + std::string algo; + + try { + + LibraryInitializer init; + + std::string header, salt_str, mac_str; + std::getline(in, header); + std::getline(in, algo); + std::getline(in, salt_str); + std::getline(in, mac_str); + + if(header != "-------- ENCRYPTED FILE --------") + { + std::cout << "ERROR: File is missing the usual header" << std::endl; + return 1; + } + + if(!have_block_cipher(algo)) + { + std::cout << "Don't know about the block cipher \"" << algo << "\"\n"; + return 1; + } + + const u32bit key_len = max_keylength_of(algo); + const u32bit iv_len = block_size_of(algo); + + std::auto_ptr<S2K> s2k(get_s2k("PBKDF2(SHA-1)")); + s2k->set_iterations(8192); + s2k->change_salt(b64_decode(salt_str)); + + SymmetricKey bc_key = s2k->derive_key(key_len, "BLK" + passphrase); + InitializationVector iv = s2k->derive_key(iv_len, "IVL" + passphrase); + SymmetricKey mac_key = s2k->derive_key(16, "MAC" + passphrase); + + Pipe pipe(new Base64_Decoder, + get_cipher(algo + "/CBC", bc_key, iv, DECRYPTION), + new Zlib_Decompression, + new Fork( + 0, + new Chain(new MAC_Filter("HMAC(SHA-1)", mac_key), + new Base64_Encoder) + ) + ); + + pipe.start_msg(); + in >> pipe; + pipe.end_msg(); + + std::string our_mac = pipe.read_all_as_string(1); + if(our_mac != mac_str) + std::cout << "WARNING: MAC in message failed to verify\n"; + + std::cout << pipe.read_all_as_string(0); + } + catch(Algorithm_Not_Found) + { + std::cout << "Don't know about the block cipher \"" << algo << "\"\n"; + return 1; + } + catch(Decoding_Error) + { + std::cout << "Bad passphrase or corrupt file\n"; + return 1; + } + catch(std::exception& e) + { + std::cout << "Exception caught: " << e.what() << std::endl; + return 1; + } + return 0; + } + +SecureVector<byte> b64_decode(const std::string& in) + { + Pipe pipe(new Base64_Decoder); + pipe.process_msg(in); + return pipe.read_all(); + } diff --git a/doc/examples/dh.cpp b/doc/examples/dh.cpp new file mode 100644 index 000000000..8dcc93f88 --- /dev/null +++ b/doc/examples/dh.cpp @@ -0,0 +1,54 @@ +/* + A simple DH example + + Written by Jack Lloyd ([email protected]), on December 24, 2003 + + This file is in the public domain +*/ +#include <botan/botan.h> +#include <botan/dh.h> +using namespace Botan; + +#include <iostream> + +int main() + { + try { + LibraryInitializer init; + + // Alice creates a DH key and sends (the public part) to Bob + DH_PrivateKey private_a(DL_Group("modp/ietf/1024")); + DH_PublicKey public_a = private_a; // Bob gets this + + // Bob creates a key with a matching group + DH_PrivateKey private_b(public_a.get_domain()); + + // Bob sends the key back to Alice + DH_PublicKey public_b = private_b; // Alice gets this + + // Both of them create a key using their private key and the other's + // public key + SymmetricKey alice_key = private_a.derive_key(public_b); + SymmetricKey bob_key = private_b.derive_key(public_a); + + if(alice_key == bob_key) + { + std::cout << "The two keys matched, everything worked\n"; + std::cout << "The shared key was: " << alice_key.as_string() << "\n"; + } + else + { + std::cout << "The two keys didn't match!\n"; + std::cout << "Alice's key was: " << alice_key.as_string() << "\n"; + std::cout << "Bob's key was: " << bob_key.as_string() << "\n"; + } + + // Now Alice and Bob hash the key and use it for something + } + catch(std::exception& e) + { + std::cout << e.what() << std::endl; + return 1; + } + return 0; + } diff --git a/doc/examples/dsa_kgen.cpp b/doc/examples/dsa_kgen.cpp new file mode 100644 index 000000000..eb3a00393 --- /dev/null +++ b/doc/examples/dsa_kgen.cpp @@ -0,0 +1,57 @@ +/* +Generate a 1024 bit DSA key and put it into a file. The public key format is +that specified by X.509, while the private key format is PKCS #8. + +The domain parameters are the ones specified as the Java default DSA +parameters. There is nothing special about these, it's just the only 1024-bit +DSA parameter set that's included in Botan at the time of this writing. The +application always reads/writes all of the domain parameters to/from the file, +so a new set could be used without any problems. We could generate a new set +for each key, or read a set of DSA params from a file and use those, but they +mostly seem like needless complications. + +Written by Jack Lloyd ([email protected]), August 5, 2002 + Updated to use X.509 and PKCS #8 formats, October 21, 2002 + +This file is in the public domain +*/ + +#include <iostream> +#include <fstream> +#include <string> +#include <botan/botan.h> +#include <botan/dsa.h> +using namespace Botan; + +int main(int argc, char* argv[]) + { + if(argc != 2) + { + std::cout << "Usage: " << argv[0] << " passphrase" << std::endl; + return 1; + } + + std::string passphrase(argv[1]); + + std::ofstream priv("dsapriv.pem"); + std::ofstream pub("dsapub.pem"); + if(!priv || !pub) + { + std::cout << "Couldn't write output files" << std::endl; + return 1; + } + + try { + LibraryInitializer init; + + DSA_PrivateKey key(DL_Group("dsa/jce/1024")); + + pub << X509::PEM_encode(key); + priv << PKCS8::PEM_encode(key, passphrase); + } + catch(std::exception& e) + { + std::cout << "Exception caught: " << e.what() << std::endl; + } + return 0; + } diff --git a/doc/examples/dsa_sign.cpp b/doc/examples/dsa_sign.cpp new file mode 100644 index 000000000..26f9e9dac --- /dev/null +++ b/doc/examples/dsa_sign.cpp @@ -0,0 +1,79 @@ +/* +Decrypt an encrypted DSA private key. Then use that key to sign a message. + +Written by Jack Lloyd ([email protected]), August 5, 2002 + Updated to use X.509 and PKCS #8 format keys, October 21, 2002 + +This file is in the public domain +*/ + +#include <iostream> +#include <iomanip> +#include <fstream> +#include <string> +#include <memory> + +#include <botan/botan.h> +#include <botan/look_pk.h> +#include <botan/dsa.h> +using namespace Botan; + +const std::string SUFFIX = ".sig"; + +int main(int argc, char* argv[]) + { + if(argc != 4) + { + std::cout << "Usage: " << argv[0] << " keyfile messagefile passphrase" + << std::endl; + return 1; + } + + try { + std::string passphrase(argv[3]); + + std::ifstream message(argv[2]); + if(!message) + { + std::cout << "Couldn't read the message file." << std::endl; + return 1; + } + + std::string outfile = argv[2] + SUFFIX; + std::ofstream sigfile(outfile.c_str()); + if(!sigfile) + { + std::cout << "Couldn't write the signature to " + << outfile << std::endl; + return 1; + } + + LibraryInitializer init; + + std::auto_ptr<PKCS8_PrivateKey> key( + PKCS8::load_key(argv[1], passphrase) + ); + + DSA_PrivateKey* dsakey = dynamic_cast<DSA_PrivateKey*>(key.get()); + + if(!dsakey) + { + std::cout << "The loaded key is not a DSA key!\n"; + return 1; + } + + Pipe pipe(new PK_Signer_Filter(get_pk_signer(*dsakey, "EMSA1(SHA-1)")), + new Base64_Encoder); + + pipe.start_msg(); + message >> pipe; + pipe.end_msg(); + + sigfile << pipe.read_all_as_string() << std::endl; + } + catch(std::exception& e) + { + std::cout << "Exception caught: " << e.what() << std::endl; + } + return 0; + } diff --git a/doc/examples/dsa_ver.cpp b/doc/examples/dsa_ver.cpp new file mode 100644 index 000000000..fb6eb7079 --- /dev/null +++ b/doc/examples/dsa_ver.cpp @@ -0,0 +1,94 @@ +/* +Grab an DSA public key from the file given as an argument, grab a signature +from another file, and verify the message (which, suprise, is also in a file). + +The signature format isn't particularly standard, but it's not bad. It's simply +the IEEE 1363 signature format, encoded into base64 with a trailing newline + +Written by Jack Lloyd ([email protected]), August 5, 2002 + Updated to use X.509 format keys, October 21, 2002 + +This file is in the public domain +*/ + +#include <iostream> +#include <iomanip> +#include <fstream> +#include <cstdlib> +#include <string> + +#include <botan/botan.h> +#include <botan/look_pk.h> +#include <botan/dsa.h> +using namespace Botan; + +SecureVector<byte> b64_decode(const std::string& in) + { + Pipe pipe(new Base64_Decoder); + pipe.process_msg(in); + return pipe.read_all(); + } + +int main(int argc, char* argv[]) + { + if(argc != 4) + { + std::cout << "Usage: " << argv[0] + << " keyfile messagefile sigfile" << std::endl; + return 1; + } + + std::ifstream message(argv[2]); + if(!message) + { + std::cout << "Couldn't read the message file." << std::endl; + return 1; + } + + std::ifstream sigfile(argv[3]); + if(!sigfile) + { + std::cout << "Couldn't read the signature file." << std::endl; + return 1; + } + + try { + std::string sigstr; + getline(sigfile, sigstr); + + LibraryInitializer init; + + std::auto_ptr<X509_PublicKey> key(X509::load_key(argv[1])); + DSA_PublicKey* dsakey = dynamic_cast<DSA_PublicKey*>(key.get()); + if(!dsakey) + { + std::cout << "The loaded key is not a DSA key!\n"; + return 1; + } + + SecureVector<byte> sig = b64_decode(sigstr); + + Pipe pipe(new PK_Verifier_Filter( + get_pk_verifier(*dsakey, "EMSA1(SHA-1)"), sig + ) + ); + + pipe.start_msg(); + message >> pipe; + pipe.end_msg(); + + byte result = 0; + pipe.read(result); + + if(result) + std::cout << "Signature verified\n"; + else + std::cout << "Signature did NOT verify\n"; + } + catch(std::exception& e) + { + std::cout << "Exception caught: " << e.what() << std::endl; + return 1; + } + return 0; + } diff --git a/doc/examples/encrypt.cpp b/doc/examples/encrypt.cpp new file mode 100644 index 000000000..c2cf2c5ba --- /dev/null +++ b/doc/examples/encrypt.cpp @@ -0,0 +1,175 @@ +/* +Encrypt a file using a block cipher in CBC mode. Compresses the plaintext +with Zlib, MACs with HMAC(SHA-1). Stores the block cipher used in the file, +so you don't have to specify it when decrypting. + +What a real application would do (and what this example should do), is test for +the presence of the Zlib module, and use it only if it's available. Then add +some marker to the stream so the other side knows whether or not the plaintext +was compressed. Bonus points for supporting multiple compression schemes. + +Another flaw is that is stores the entire ciphertext in memory, so if the file +you're encrypting is 1 Gb... you better have a lot of RAM. + +Based on the base64 example, of all things + +Written by Jack Lloyd ([email protected]) on August 5, 2002 + +This file is in the public domain +*/ +#include <fstream> +#include <iostream> +#include <string> +#include <vector> +#include <cstring> + +#include <botan/botan.h> + +#if defined(BOTAN_EXT_COMPRESSOR_ZLIB) + #include <botan/zlib.h> +#else + #error "You didn't compile the zlib module into Botan" +#endif + +using namespace Botan; + +std::string b64_encode(const SecureVector<byte>&); + +int main(int argc, char* argv[]) + { + if(argc < 2) + { + std::cout << "Usage: " << argv[0] << " [-c algo] -p passphrase file\n" + " -p : Use this passphrase to encrypt\n" + " -c : Encrypt with block cipher 'algo' (default 3DES)\n"; + return 1; + } + + std::string algo = "TripleDES"; + std::string filename, passphrase; + + // Holy hell, argument processing is a PITA + for(int j = 1; argv[j] != 0; j++) + { + if(std::strcmp(argv[j], "-c") == 0) + { + if(argv[j+1]) + { + algo = argv[j+1]; + j++; + } + else + { + std::cout << "No argument for -c option" << std::endl; + return 1; + } + } + else if(std::strcmp(argv[j], "-p") == 0) + { + if(argv[j+1]) + { + passphrase = argv[j+1]; + j++; + } + else + { + std::cout << "No argument for -p option" << std::endl; + return 1; + } + } + else + { + if(filename != "") + { + std::cout << "You can only specify one file at a time\n"; + return 1; + } + filename = argv[j]; + } + } + + if(passphrase == "") + { + std::cout << "You have to specify a passphrase!" << std::endl; + return 1; + } + + std::ifstream in(filename.c_str()); + if(!in) + { + std::cout << "ERROR: couldn't open " << filename << std::endl; + return 1; + } + + std::string outfile = filename + ".enc"; + std::ofstream out(outfile.c_str()); + if(!out) + { + std::cout << "ERROR: couldn't open " << outfile << std::endl; + return 1; + } + + try { + + LibraryInitializer init; + + if(!have_block_cipher(algo)) + { + std::cout << "Don't know about the block cipher \"" << algo << "\"\n"; + return 1; + } + + const u32bit key_len = max_keylength_of(algo); + const u32bit iv_len = block_size_of(algo); + + std::auto_ptr<S2K> s2k(get_s2k("PBKDF2(SHA-1)")); + s2k->set_iterations(8192); + s2k->new_random_salt(8); + + SymmetricKey bc_key = s2k->derive_key(key_len, "BLK" + passphrase); + InitializationVector iv = s2k->derive_key(iv_len, "IVL" + passphrase); + SymmetricKey mac_key = s2k->derive_key(16, "MAC" + passphrase); + + // Just to be all fancy we even write a (simple) header. + out << "-------- ENCRYPTED FILE --------" << std::endl; + out << algo << std::endl; + out << b64_encode(s2k->current_salt()) << std::endl; + + Pipe pipe(new Fork( + new Chain(new MAC_Filter("HMAC(SHA-1)", mac_key), + new Base64_Encoder + ), + new Chain(new Zlib_Compression, + get_cipher(algo + "/CBC", bc_key, iv, ENCRYPTION), + new Base64_Encoder(true) + ) + ) + ); + + pipe.start_msg(); + in >> pipe; + pipe.end_msg(); + + out << pipe.read_all_as_string(0) << std::endl; + out << pipe.read_all_as_string(1); + + } + catch(Algorithm_Not_Found) + { + std::cout << "Don't know about the block cipher \"" << algo << "\"\n"; + return 1; + } + catch(std::exception& e) + { + std::cout << "Exception caught: " << e.what() << std::endl; + return 1; + } + return 0; + } + +std::string b64_encode(const SecureVector<byte>& in) + { + Pipe pipe(new Base64_Encoder); + pipe.process_msg(in); + return pipe.read_all_as_string(); + } diff --git a/doc/examples/fips140.cpp b/doc/examples/fips140.cpp new file mode 100644 index 000000000..46e0db4b0 --- /dev/null +++ b/doc/examples/fips140.cpp @@ -0,0 +1,59 @@ +/* + A minimal FIPS-140 application. + + Written by Jack Lloyd ([email protected]), on December 16-19, 2003 + + This file is in the public domain +*/ + +#include <botan/botan.h> +#include <botan/fips140.h> +using namespace Botan; + +#include <iostream> +#include <fstream> + +int main(int, char* argv[]) + { + const std::string EDC_SUFFIX = ".edc"; + + try { + LibraryInitializer init; /* automatically does startup self tests */ + + // you can also do self tests on demand, like this: + if(!FIPS140::passes_self_tests()) + throw Self_Test_Failure("FIPS-140 startup tests"); + + /* + Here, we just check argv[0] and assume that it works. You can use + various extremely nonportable APIs on some Unices (dladdr, to name one) + to find out the real name (I presume there are similiarly hairy ways of + doing it on Windows). We then assume the EDC (Error Detection Code, aka + a hash) is stored in argv[0].edc + + Remember: argv[0] can be easily spoofed. Don't trust it for real. + + You can also do various nasty things and find out the path of the + shared library you are linked with, and check that hash. + */ + std::string exe_path = argv[0]; + std::string edc_path = exe_path + EDC_SUFFIX; + std::ifstream edc_file(edc_path.c_str()); + std::string edc; + std::getline(edc_file, edc); + + std::cout << "Our EDC is " << edc << std::endl; + + bool good = FIPS140::good_edc(exe_path, edc); + + if(good) + std::cout << "Our EDC matches" << std::endl; + else + std::cout << "Our EDC is bad" << std::endl; + } + catch(std::exception& e) + { + std::cout << "Exception caught: " << e.what() << std::endl; + } + return 0; + } diff --git a/doc/examples/hash.cpp b/doc/examples/hash.cpp new file mode 100644 index 000000000..a97cd6082 --- /dev/null +++ b/doc/examples/hash.cpp @@ -0,0 +1,64 @@ +/* +Prints the message digest of files, using an arbitrary hash function +chosen by the user. This is less flexible that I might like, for example: + ./hash sha1 some_file [or md5 or sha-1 or ripemd160 or ...] +will not work, cause the name lookup is case-sensitive. Oh well... + +Written by Jack Lloyd ([email protected]), on August 4, 2002 + - December 16, 2003: "Fixed" to accept "sha1" or "md5" as a hash name + +This file is in the public domain +*/ + +#include <iostream> +#include <fstream> +#include <botan/botan.h> + +int main(int argc, char* argv[]) + { + if(argc < 3) + { + std::cout << "Usage: " << argv[0] << " digest <filenames>" << std::endl; + return 1; + } + + Botan::LibraryInitializer init; + + std::string hash = argv[1]; + /* a couple of special cases, kind of a crock */ + if(hash == "sha1") hash = "SHA-1"; + if(hash == "md5") hash = "MD5"; + + try { + if(!Botan::have_hash(hash)) + { + std::cout << "Unknown hash \"" << argv[1] << "\"" << std::endl; + return 1; + } + + Botan::Pipe pipe(new Botan::Hash_Filter(hash), + new Botan::Hex_Encoder); + + int skipped = 0; + for(int j = 2; argv[j] != 0; j++) + { + std::ifstream file(argv[j]); + if(!file) + { + std::cout << "ERROR: could not open " << argv[j] << std::endl; + skipped++; + continue; + } + pipe.start_msg(); + file >> pipe; + pipe.end_msg(); + pipe.set_default_msg(j-2-skipped); + std::cout << pipe << " " << argv[j] << std::endl; + } + } + catch(std::exception& e) + { + std::cout << "Exception caught: " << e.what() << std::endl; + } + return 0; + } diff --git a/doc/examples/hash_fd.cpp b/doc/examples/hash_fd.cpp new file mode 100644 index 000000000..19d744287 --- /dev/null +++ b/doc/examples/hash_fd.cpp @@ -0,0 +1,70 @@ +/* +Written by Jack Lloyd ([email protected]), on Prickle-Prickle, +the 10th of Bureaucracy, 3167. + +This file is in the public domain + +This is just like the normal hash application, but uses the Unix I/O system +calls instead of C++ iostreams. Previously, this version was much faster and +smaller, but GCC 3.1's libstdc++ seems to have been improved enough that the +difference is now fairly minimal. + +Nicely enough, doing the change required changing only about 3 lines of code. + +Note that this requires you to be on a machine running some sort of Unix. Well, +I guess any POSIX.1 compliant OS (in theory). +*/ + +#include <iostream> +#include <botan/botan.h> + +#if !defined(BOTAN_EXT_PIPE_UNIXFD_IO) + #error "You didn't compile the pipe_unixfd module into Botan" +#endif + +#include <fcntl.h> +#include <unistd.h> + +int main(int argc, char* argv[]) + { + if(argc < 3) + { + std::cout << "Usage: " << argv[0] << " digest <filenames>" << std::endl; + return 1; + } + + Botan::LibraryInitializer init; + + try { + Botan::Pipe pipe(new Botan::Hash_Filter(argv[1]), + new Botan::Hex_Encoder); + + int skipped = 0; + for(int j = 2; argv[j] != 0; j++) + { + int file = open(argv[j], O_RDONLY); + if(file == -1) + { + std::cout << "ERROR: could not open " << argv[j] << std::endl; + skipped++; + continue; + } + pipe.start_msg(); + file >> pipe; + pipe.end_msg(); + close(file); + pipe.set_default_msg(j-2-skipped); + std::cout << pipe << " " << argv[j] << std::endl; + } + } + catch(Botan::Algorithm_Not_Found) + { + std::cout << "Don't know about the hash function \"" << argv[1] << "\"" + << std::endl; + } + catch(std::exception& e) + { + std::cout << "Exception caught: " << e.what() << std::endl; + } + return 0; + } diff --git a/doc/examples/hasher.cpp b/doc/examples/hasher.cpp new file mode 100644 index 000000000..5ba982fc0 --- /dev/null +++ b/doc/examples/hasher.cpp @@ -0,0 +1,58 @@ +/* +A Botan example application which emulates a +poorly written version of "gpg --print-md" + +Written by Jack Lloyd ([email protected]), quite a while ago (as of June +2001) + +This file is in the public domain +*/ +#include <fstream> +#include <iostream> +#include <string> +#include <botan/botan.h> + +int main(int argc, char* argv[]) + { + if(argc < 2) + { + std::cout << "Usage: " << argv[0] << " <filenames>" << std::endl; + return 1; + } + + Botan::LibraryInitializer init; + + const int COUNT = 3; + std::string name[COUNT] = { "MD5", "SHA-1", "RIPEMD-160" }; + + for(int j = 1; argv[j] != 0; j++) + { + Botan::Filter* hash[COUNT] = { + new Botan::Chain(new Botan::Hash_Filter(name[0]), + new Botan::Hex_Encoder), + new Botan::Chain(new Botan::Hash_Filter(name[1]), + new Botan::Hex_Encoder), + new Botan::Chain(new Botan::Hash_Filter(name[2]), + new Botan::Hex_Encoder) + }; + + Botan::Pipe pipe(new Botan::Fork(hash, COUNT)); + + std::ifstream file(argv[j]); + if(!file) + { + std::cout << "ERROR: could not open " << argv[j] << std::endl; + continue; + } + pipe.start_msg(); + file >> pipe; + pipe.end_msg(); + file.close(); + for(int k = 0; k != COUNT; k++) + { + pipe.set_default_msg(k); + std::cout << name[k] << "(" << argv[j] << ") = " << pipe << std::endl; + } + } + return 0; + } diff --git a/doc/examples/hasher2.cpp b/doc/examples/hasher2.cpp new file mode 100644 index 000000000..12d3c853d --- /dev/null +++ b/doc/examples/hasher2.cpp @@ -0,0 +1,72 @@ +/* +Identical to hasher.cpp, but uses Pipe in a different way. + +Note this tends to be much less efficient than hasher.cpp, because it does +three passes over the file. For a small file, it doesn't really matter. But for +a large file, or for something you can't re-read easily (socket, stdin, ...) +this is a bad idea. + +Written by Jack Lloyd ([email protected]), Feb 8 2001 + +This file is in the public domain +*/ +#include <fstream> +#include <iostream> +#include <string> +#include <botan/botan.h> + +int main(int argc, char* argv[]) + { + if(argc < 2) + { + std::cout << "Usage: " << argv[0] << " <filenames>" << std::endl; + return 1; + } + + Botan::LibraryInitializer init; + + const int COUNT = 3; + std::string name[COUNT] = { "MD5", "SHA-1", "RIPEMD-160" }; + + Botan::Pipe pipe; + + int skipped = 0; + for(int j = 1; argv[j] != 0; j++) + { + Botan::Filter* hash[COUNT] = { + new Botan::Hash_Filter(name[0]), + new Botan::Hash_Filter(name[1]), + new Botan::Hash_Filter(name[2]), + }; + + std::ifstream file(argv[j]); + if(!file) + { + std::cout << "ERROR: could not open " << argv[j] << std::endl; + skipped++; + continue; + } + for(int k = 0; k != COUNT; k++) + { + pipe.reset(); + pipe.append(hash[k]); + pipe.append(new Botan::Hex_Encoder); + pipe.start_msg(); + + // trickiness: the >> op reads until EOF, but seekg won't work + // unless we're in the "good" state (which EOF is not). + file.clear(); + file.seekg(0, std::ios::beg); + file >> pipe; + pipe.end_msg(); + } + file.close(); + for(int k = 0; k != COUNT; k++) + { + std::string out = pipe.read_all_as_string(COUNT*(j-1-skipped) + k); + std::cout << name[k] << "(" << argv[j] << ") = " << out << std::endl; + } + } + + return 0; + } diff --git a/doc/examples/pkcs10.cpp b/doc/examples/pkcs10.cpp new file mode 100644 index 000000000..4639353df --- /dev/null +++ b/doc/examples/pkcs10.cpp @@ -0,0 +1,68 @@ +/* +Generate a 1024 bit RSA key, and then create a PKCS #10 certificate request for +that key. The private key will be stored as an encrypted PKCS #8 object, and +stored in another file. + +Written by Jack Lloyd ([email protected]), April 7, 2003 + +This file is in the public domain +*/ +#include <botan/init.h> +#include <botan/x509self.h> +#include <botan/rsa.h> +#include <botan/dsa.h> +using namespace Botan; + +#include <iostream> +#include <fstream> + +int main(int argc, char* argv[]) + { + if(argc != 6) + { + std::cout << "Usage: " << argv[0] << + " passphrase name country_code organization email" << std::endl; + return 1; + } + + try { + LibraryInitializer init; + + RSA_PrivateKey priv_key(1024); + // If you want a DSA key instead of RSA, comment out the above line and + // uncomment this one: + //DSA_PrivateKey priv_key(DL_Group("dsa/jce/1024")); + + std::ofstream key_file("private.pem"); + key_file << PKCS8::PEM_encode(priv_key, argv[1]); + + X509_Cert_Options opts; + + opts.common_name = argv[2]; + opts.country = argv[3]; + opts.organization = argv[4]; + opts.email = argv[5]; + + /* Some hard-coded options, just to give you an idea of what's there */ + opts.challenge = "a fixed challenge passphrase"; + opts.locality = "Baltimore"; + opts.state = "MD"; + opts.org_unit = "Testing"; + opts.add_ex_constraint("PKIX.ClientAuth"); + opts.add_ex_constraint("PKIX.IPsecUser"); + opts.add_ex_constraint("PKIX.EmailProtection"); + + opts.xmpp = "[email protected]"; + + PKCS10_Request req = X509::create_cert_req(opts, priv_key); + + std::ofstream req_file("req.pem"); + req_file << req.PEM_encode(); + } + catch(std::exception& e) + { + std::cout << e.what() << std::endl; + return 1; + } + return 0; + } diff --git a/doc/examples/readme.txt b/doc/examples/readme.txt new file mode 100644 index 000000000..48686db71 --- /dev/null +++ b/doc/examples/readme.txt @@ -0,0 +1,77 @@ +This directory contains some simple example applications for the Botan crypto +library. If you want to see something a bit more complicated, check out the +stuff in the checks/ directory. Both it and the files in this directory are in +the public domain, and you may do with them however you please. + +The makefile assumes that you built the library with g++; you'll have to change +it if this assumption proves incorrect. + +Some of these examples will not build on all configurations of the library, +particularly 'bzip', 'encrypt', 'decrypt', and 'hash_fd', as they require +various extensions. + +The examples are fairly small (50-150 lines). And that's with argument +processing, I/O, error checking, etc (which counts for 40% or more of most of +them). This is partially to make them easy to understand, and partially because +I'm lazy. For the most part, the examples cover the stuff a 'regular' +application might need. + +Feel free to contribute new examples. You too can gain fame and fortune by +writing example apps for obscure libraries! + +The examples are: + +* X.509 examples +-------- +ca: A (very) simple CA application + +x509info: Prints some information about an X.509 certificate + +pkcs10: Generates a PKCS #10 certificate request for a 1024 bit RSA key + +self_sig: Generates a self-signed X.509v3 certificate with a 1024 bit RSA key +-------- + +* RSA examples (also uses X.509, PKCS #8, block ciphers, MACs, S2K algorithms) +-------- +rsa_kgen: Generate an RSA key, encrypt the private key with a passphrase, + output the keys to a pair of files +rsa_enc: Take a public key (generated by rsa_kgen) and encrypt a file + using CAST-128, MAC it with HMAC(SHA-1) +rsa_dec: Decrypt a file encrypted by rsa_enc + +* DSA examples (also uses X.509, PKCS #8) +-------- +dsa_kgen: Generates a DSA key, encrypts the private key with a passphrase + and stores it in PKCS #8 format. +dsa_sign: Produce a DSA signature for a file. Uses SHA-1 +dsa_ver: Verify a message signed with dsa_sign + +* Encryption examples +-------- +encrypt: Encrypt a file in CBC mode with a block cipher of your choice. Adds + a MAC for authentication, and compresses the plaintext with Zlib. + +decrypt: Decrypt the result of 'encrypt' + +xor_ciph: Shows how to add a new algorithm from application code + +* Hash function examples (also shows different methods of using Pipe) +-------- +hash: Print digests of files, using any chosen hash function + +hash_fd: Same as hash, except that it uses Unix file I/O. Requires the + pipe_unixfd extension + +hasher: Print MD5, SHA-1, and RIPEMD-160 digests of files + +hasher2: Same as hasher, just shows an alternate method + +stack: A demonstration of some more advanced Pipe functionality. Prints + MD5 hashes + +* Misc examples +-------- +base64: Simple base64 encoding/decoding tool + +bzip: Bzip2 compression/decompression. diff --git a/doc/examples/rsa_dec.cpp b/doc/examples/rsa_dec.cpp new file mode 100644 index 000000000..d50f6781a --- /dev/null +++ b/doc/examples/rsa_dec.cpp @@ -0,0 +1,122 @@ +/* +Decrypt an encrypted RSA private key. Then use that key to decrypt a +message. This program can decrypt messages generated by rsa_enc, and uses the +same key format as that generated by rsa_kgen. + +Written by Jack Lloyd ([email protected]), June 3-5, 2002 + +This file is in the public domain +*/ + +#include <iostream> +#include <fstream> +#include <string> + +#include <botan/botan.h> +#include <botan/look_pk.h> // for get_kdf +#include <botan/rsa.h> +using namespace Botan; + +SecureVector<byte> b64_decode(const std::string&); +SymmetricKey derive_key(const std::string&, const SymmetricKey&, u32bit); + +const std::string SUFFIX = ".enc"; + +int main(int argc, char* argv[]) + { + if(argc != 4) + { + std::cout << "Usage: " << argv[0] << " keyfile messagefile passphrase" + << std::endl; + return 1; + } + + try { + + LibraryInitializer init; + + std::auto_ptr<PKCS8_PrivateKey> key(PKCS8::load_key(argv[1], argv[3])); + RSA_PrivateKey* rsakey = dynamic_cast<RSA_PrivateKey*>(key.get()); + if(!rsakey) + { + std::cout << "The loaded key is not a RSA key!\n"; + return 1; + } + + std::ifstream message(argv[2]); + if(!message) + { + std::cout << "Couldn't read the message file." << std::endl; + return 1; + } + + std::string outfile(argv[2]); + outfile = outfile.replace(outfile.find(SUFFIX), SUFFIX.length(), ""); + + std::ofstream plaintext(outfile.c_str()); + if(!plaintext) + { + std::cout << "Couldn't write the plaintext to " + << outfile << std::endl; + return 1; + } + + std::string enc_masterkey_str; + std::getline(message, enc_masterkey_str); + std::string mac_str; + std::getline(message, mac_str); + + SecureVector<byte> enc_masterkey = b64_decode(enc_masterkey_str); + + std::auto_ptr<PK_Decryptor> decryptor(get_pk_decryptor(*rsakey, + "EME1(SHA-1)")); + SecureVector<byte> masterkey = decryptor->decrypt(enc_masterkey); + + SymmetricKey cast_key = derive_key("CAST", masterkey, 16); + InitializationVector iv = derive_key("IV", masterkey, 8); + SymmetricKey mac_key = derive_key("MAC", masterkey, 16); + + Pipe pipe(new Base64_Decoder, + get_cipher("CAST-128/CBC/PKCS7", cast_key, iv, DECRYPTION), + new Fork( + 0, + new Chain( + new MAC_Filter("HMAC(SHA-1)", mac_key, 12), + new Base64_Encoder + ) + ) + ); + + pipe.start_msg(); + message >> pipe; + pipe.end_msg(); + + std::string our_mac = pipe.read_all_as_string(1); + + if(our_mac != mac_str) + std::cout << "WARNING: MAC in message failed to verify\n"; + + plaintext << pipe.read_all_as_string(0); + } + catch(std::exception& e) + { + std::cout << "Exception caught: " << e.what() << std::endl; + return 1; + } + return 0; + } + +SecureVector<byte> b64_decode(const std::string& in) + { + Pipe pipe(new Base64_Decoder); + pipe.process_msg(in); + return pipe.read_all(); + } + +SymmetricKey derive_key(const std::string& param, + const SymmetricKey& masterkey, + u32bit outputlength) + { + std::auto_ptr<KDF> kdf(get_kdf("KDF2(SHA-1)")); + return kdf->derive_key(outputlength, masterkey.bits_of(), param); + } diff --git a/doc/examples/rsa_enc.cpp b/doc/examples/rsa_enc.cpp new file mode 100644 index 000000000..49b62989c --- /dev/null +++ b/doc/examples/rsa_enc.cpp @@ -0,0 +1,149 @@ +/* + Grab an RSA public key from the file given as an argument, grab a message + from another file, and encrypt the message. + + Algorithms used: + RSA with EME1(SHA-1) padding to encrypt the master key + CAST-128 in CBC mode with PKCS#7 padding to encrypt the message. + HMAC with SHA-1 is used to authenticate the message + + The keys+IV used are derived from the master key (the thing that's encrypted + with RSA) using KDF2(SHA-1). The 3 outputs of KDF2 are parameterized by P, + where P is "CAST", "IV" or "MAC", in order to make each key/IV unique. + + The format is: + 1) First line is the master key, encrypted with the recipients public key + using EME1(SHA-1), and then base64 encoded. + 2) Second line is the first 96 bits (12 bytes) of the HMAC(SHA-1) of + the _plaintext_ + 3) Following lines are base64 encoded ciphertext (CAST-128 as described), + each broken after ~72 characters. + +Written by Jack Lloyd ([email protected]), June 3, 2002 + Updated to use KDF2, September 8, 2002 + Updated to read X.509 keys, October 21, 2002 + +This file is in the public domain +*/ + +#include <iostream> +#include <fstream> +#include <string> +#include <memory> + +#include <botan/botan.h> +#include <botan/look_pk.h> +#include <botan/rsa.h> +using namespace Botan; + +std::string b64_encode(const SecureVector<byte>&); +SymmetricKey derive_key(const std::string&, const SymmetricKey&, u32bit); + +int main(int argc, char* argv[]) + { + if(argc != 3) + { + std::cout << "Usage: " << argv[0] << " keyfile messagefile" << std::endl; + return 1; + } + + std::ifstream message(argv[2]); + if(!message) + { + std::cout << "Couldn't read the message file." << std::endl; + return 1; + } + + std::string output_name(argv[2]); + output_name += ".enc"; + std::ofstream ciphertext(output_name.c_str()); + if(!ciphertext) + { + std::cout << "Couldn't write the ciphertext to " << output_name + << std::endl; + return 1; + } + + try { + + LibraryInitializer init; + + std::auto_ptr<X509_PublicKey> key(X509::load_key(argv[1])); + RSA_PublicKey* rsakey = dynamic_cast<RSA_PublicKey*>(key.get()); + if(!rsakey) + { + std::cout << "The loaded key is not a RSA key!\n"; + return 1; + } + + std::auto_ptr<PK_Encryptor> encryptor(get_pk_encryptor(*rsakey, + "EME1(SHA-1)")); + + /* Generate the master key (the other keys are derived from this) + + Basically, make the key as large as can be encrypted by this key, up + to a limit of 256 bits. For 512 bit keys, the master key will be >160 + bits. A >600 bit key will use the full 256 bit master key. + + In theory, this is not enough, because we derive 16+16+8=40 bytes of + secrets (if you include the IV) using the master key, so they are not + statistically indepedent. Practically speaking I don't think this is + a problem. + */ + SymmetricKey masterkey(std::min(32U, encryptor->maximum_input_size())); + + SymmetricKey cast_key = derive_key("CAST", masterkey, 16); + SymmetricKey mac_key = derive_key("MAC", masterkey, 16); + SymmetricKey iv = derive_key("IV", masterkey, 8); + + SecureVector<byte> encrypted_key = + encryptor->encrypt(masterkey.bits_of()); + + ciphertext << b64_encode(encrypted_key) << std::endl; + + Pipe pipe(new Fork( + new Chain( + get_cipher("CAST-128/CBC/PKCS7", cast_key, iv, + ENCRYPTION), + new Base64_Encoder(true) // true == do linebreaking + ), + new Chain( + new MAC_Filter("HMAC(SHA-1)", mac_key, 12), + new Base64_Encoder + ) + ) + ); + + pipe.start_msg(); + message >> pipe; + pipe.end_msg(); + + /* Write the MAC as the second line. That way we can pull it off right + from the start, and feed the rest of the file right into a pipe on the + decrypting end. + */ + + ciphertext << pipe.read_all_as_string(1) << std::endl; + ciphertext << pipe.read_all_as_string(0); + } + catch(std::exception& e) + { + std::cout << "Exception: " << e.what() << std::endl; + } + return 0; + } + +std::string b64_encode(const SecureVector<byte>& in) + { + Pipe pipe(new Base64_Encoder); + pipe.process_msg(in); + return pipe.read_all_as_string(); + } + +SymmetricKey derive_key(const std::string& param, + const SymmetricKey& masterkey, + u32bit outputlength) + { + std::auto_ptr<KDF> kdf(get_kdf("KDF2(SHA-1)")); + return kdf->derive_key(outputlength, masterkey.bits_of(), param); + } diff --git a/doc/examples/rsa_kgen.cpp b/doc/examples/rsa_kgen.cpp new file mode 100644 index 000000000..f2601101e --- /dev/null +++ b/doc/examples/rsa_kgen.cpp @@ -0,0 +1,55 @@ +/* +Generate an RSA key of a specified bitlength, and put it into a pair of key +files. One is the public key in X.509 format (PEM encoded), the private key is +in PKCS #8 format (also PEM encoded). + +Written by Jack Lloyd ([email protected]), June 2-3, 2002 + Updated to use X.509 and PKCS #8 on October 21, 2002 + +This file is in the public domain +*/ + +#include <iostream> +#include <fstream> +#include <string> +#include <botan/botan.h> +#include <botan/rsa.h> +using namespace Botan; + +int main(int argc, char* argv[]) + { + if(argc != 3) + { + std::cout << "Usage: " << argv[0] << " bitsize passphrase" << std::endl; + return 1; + } + + u32bit bits = std::atoi(argv[1]); + if(bits < 512 || bits > 4096) + { + std::cout << "Invalid argument for bitsize" << std::endl; + return 1; + } + + std::string passphrase(argv[2]); + + std::ofstream pub("rsapub.pem"); + std::ofstream priv("rsapriv.pem"); + if(!priv || !pub) + { + std::cout << "Couldn't write output files" << std::endl; + return 1; + } + + try { + LibraryInitializer init; + RSA_PrivateKey key(bits); + pub << X509::PEM_encode(key); + priv << PKCS8::PEM_encode(key, passphrase); + } + catch(std::exception& e) + { + std::cout << "Exception caught: " << e.what() << std::endl; + } + return 0; + } diff --git a/doc/examples/self_sig.cpp b/doc/examples/self_sig.cpp new file mode 100644 index 000000000..c117ff3aa --- /dev/null +++ b/doc/examples/self_sig.cpp @@ -0,0 +1,76 @@ +/* +Generate a 1024 bit RSA key, and then create a self-signed X.509v3 certificate +with that key. If the do_CA variable is set to true, then it will be marked for +CA use, otherwise it will get extensions appropriate for use with a client +certificate. The private key is stored as an encrypted PKCS #8 object in +another file. + +Written by Jack Lloyd ([email protected]), April 7, 2003 + +This file is in the public domain +*/ +#include <botan/botan.h> +#include <botan/x509self.h> +#include <botan/rsa.h> +#include <botan/dsa.h> +using namespace Botan; + +#include <iostream> +#include <fstream> + +int main(int argc, char* argv[]) + { + if(argc != 7) + { + std::cout << "Usage: " << argv[0] + << " passphrase [CA|user] name country_code organization email" + << std::endl; + return 1; + } + + LibraryInitializer init; + + std::string CA_flag = argv[2]; + bool do_CA = false; + + if(CA_flag == "CA") do_CA = true; + else if(CA_flag == "user") do_CA = false; + else + { + std::cout << "Bad flag for CA/user switch: " << CA_flag << std::endl; + return 1; + } + + try { + RSA_PrivateKey key(1024); + //DSA_PrivateKey key(DL_Group("dsa/jce/1024")); + + std::ofstream priv_key("private.pem"); + priv_key << PKCS8::PEM_encode(key, argv[1]); + + X509_Cert_Options opts; + + opts.common_name = argv[3]; + opts.country = argv[4]; + opts.organization = argv[5]; + opts.email = argv[6]; + /* Fill in other values of opts here */ + + //opts.xmpp = "[email protected]"; + + if(do_CA) + opts.CA_key(); + + X509_Certificate cert = X509::create_self_signed_cert(opts, key); + + std::ofstream cert_file("cert.pem"); + cert_file << cert.PEM_encode(); + } + catch(std::exception& e) + { + std::cout << "Exception: " << e.what() << std::endl; + return 1; + } + + return 0; + } diff --git a/doc/examples/stack.cpp b/doc/examples/stack.cpp new file mode 100644 index 000000000..1522b05f5 --- /dev/null +++ b/doc/examples/stack.cpp @@ -0,0 +1,86 @@ +/* +An Botan example application showing how to use the pop and prepend functions +of Pipe. Based on the md5 example. It's output should always be identical to +such. + +Written by Jack Lloyd ([email protected]), Feb 3, 2002 + +This file is in the public domain +*/ + +#include <iostream> +#include <fstream> +#include <botan/botan.h> + +int main(int argc, char* argv[]) + { + if(argc < 2) + { + std::cout << "Usage: " << argv[0] << " <filenames>" << std::endl; + return 1; + } + + Botan::LibraryInitializer init; + + // this is a pretty vacuous example, but it's useful as a test + Botan::Pipe pipe; + + // CPS == Current Pipe Status, ie what Filters are set up + + pipe.prepend(new Botan::Hash_Filter("MD5")); + // CPS: MD5 + + pipe.prepend(new Botan::Hash_Filter("RIPEMD-160")); + // CPS: RIPEMD-160 | MD5 + + pipe.prepend(new Botan::Chain( + new Botan::Hash_Filter("RIPEMD-160"), + new Botan::Hash_Filter("RIPEMD-160"))); + // CPS: (RIPEMD-160 | RIPEMD-160) | RIPEMD-160 | MD5 + + pipe.pop(); // will pop everything inside the Chain as well as Chain itself + // CPS: RIPEMD-160 | MD5 + + pipe.pop(); // will get rid of the RIPEMD-160 Hash_Filter + // CPS: MD5 + + pipe.prepend(new Botan::Hash_Filter("SHA-1")); + // CPS: SHA-1 | MD5 + + pipe.append(new Botan::Hex_Encoder); + // CPS: SHA-1 | MD5 | Hex_Encoder + + pipe.prepend(new Botan::Hash_Filter("SHA-1")); + // CPS: SHA-1 | SHA-1 | MD5 | Hex_Encoder + + pipe.pop(); // Get rid of the Hash_Filter(SHA-1) + pipe.pop(); // Get rid of the other Hash_Filter(SHA-1) + // CPS: MD5 | Hex_Encoder + // The Hex_Encoder is safe because it is at the end of the Pipe, + // and pop() pulls off the Filter that is at the start. + + pipe.prepend(new Botan::Hash_Filter("RIPEMD-160")); + // CPS: RIPEMD-160 | MD5 | Hex_Encoder + + pipe.pop(); // Get rid of that last prepended Hash_Filter(RIPEMD-160) + // CPS: MD5 | Hex_Encoder + + int skipped = 0; + for(int j = 1; argv[j] != 0; j++) + { + std::ifstream file(argv[j]); + if(!file) + { + std::cout << "ERROR: could not open " << argv[j] << std::endl; + skipped++; + continue; + } + pipe.start_msg(); + file >> pipe; + pipe.end_msg(); + file.close(); + pipe.set_default_msg(j-1-skipped); + std::cout << pipe << " " << argv[j] << std::endl; + } + return 0; + } diff --git a/doc/examples/x509info.cpp b/doc/examples/x509info.cpp new file mode 100644 index 000000000..cbb9c0a11 --- /dev/null +++ b/doc/examples/x509info.cpp @@ -0,0 +1,142 @@ +/* + Read an X.509 certificate, and print various things about it + + Written by Jack Lloyd, March 23 2003 + - October 31, 2003: Prints the public key + - November 1, 2003: Removed the -d flag; it can tell automatically now + + This file is in the public domain +*/ +#include <botan/botan.h> +#include <botan/x509cert.h> +#include <botan/oids.h> +using namespace Botan; + +#include <iostream> + +std::string to_hex(const SecureVector<byte>& bin) + { + Pipe pipe(new Hex_Encoder); + pipe.process_msg(bin); + if(pipe.remaining()) + return pipe.read_all_as_string(); + else + return "(none)"; + } + +void do_print(const std::string& what, const std::string& val) + { + if(val == "") + return; + + std::cout << " " << what << ": " << val << std::endl; + } + +void do_subject(const X509_Certificate& cert, const std::string& what) + { + do_print(what, cert.subject_info(what)); + } + +void do_issuer(const X509_Certificate& cert, const std::string& what) + { + do_print(what, cert.issuer_info(what)); + } + +int main(int argc, char* argv[]) + { + if(argc != 2) + { + std::cout << "Usage: " << argv[0] << " <x509cert>\n"; + return 1; + } + + try { + LibraryInitializer init; + + X509_Certificate cert(argv[1]); + + std::cout << "Version: " << cert.x509_version() << std::endl; + + std::cout << "Subject" << std::endl; + do_subject(cert, "Name"); + do_subject(cert, "Email"); + do_subject(cert, "Organization"); + do_subject(cert, "Organizational Unit"); + do_subject(cert, "Locality"); + do_subject(cert, "State"); + do_subject(cert, "Country"); + do_subject(cert, "PKIX.XMPPAddr"); + + std::cout << "Issuer" << std::endl; + do_issuer(cert, "Name"); + do_issuer(cert, "Email"); + do_issuer(cert, "Organization"); + do_issuer(cert, "Organizational Unit"); + do_issuer(cert, "Locality"); + do_issuer(cert, "State"); + do_issuer(cert, "Country"); + + std::cout << "Validity" << std::endl; + + std::cout << " Not before: " << cert.start_time() << std::endl; + std::cout << " Not after: " << cert.end_time() << std::endl; + + std::cout << "Constraints" << std::endl; + Key_Constraints constraints = cert.constraints(); + if(constraints == NO_CONSTRAINTS) + std::cout << "No constraints" << std::endl; + else + { + if(constraints & DIGITAL_SIGNATURE) + std::cout << " Digital Signature\n"; + if(constraints & NON_REPUDIATION) + std::cout << " Non-Repuidation\n"; + if(constraints & KEY_ENCIPHERMENT) + std::cout << " Key Encipherment\n"; + if(constraints & DATA_ENCIPHERMENT) + std::cout << " Data Encipherment\n"; + if(constraints & KEY_AGREEMENT) + std::cout << " Key Agreement\n"; + if(constraints & KEY_CERT_SIGN) + std::cout << " Cert Sign\n"; + if(constraints & CRL_SIGN) + std::cout << " CRL Sign\n"; + } + + std::vector<OID> policies = cert.policies(); + if(policies.size()) + { + std::cout << "Policies: " << std::endl; + for(u32bit j = 0; j != policies.size(); j++) + std::cout << " " << OIDS::lookup(policies[j]) << std::endl; + } + + std::vector<OID> ex_constraints = cert.ex_constraints(); + if(ex_constraints.size()) + { + std::cout << "Extended Constraints: " << std::endl; + for(u32bit j = 0; j != ex_constraints.size(); j++) + std::cout << " " << OIDS::lookup(ex_constraints[j]) << std::endl; + } + + std::cout << "Signature algorithm: " << + OIDS::lookup(cert.signature_algorithm().oid) << std::endl; + + std::cout << "Serial: " + << to_hex(cert.serial_number()) << std::endl; + std::cout << "Authority keyid: " + << to_hex(cert.authority_key_id()) << std::endl; + std::cout << "Subject keyid: " + << to_hex(cert.subject_key_id()) << std::endl; + + X509_PublicKey* pubkey = cert.subject_public_key(); + std::cout << "Public Key:\n" << X509::PEM_encode(*pubkey); + delete pubkey; + } + catch(std::exception& e) + { + std::cout << e.what() << std::endl; + return 1; + } + return 0; + } diff --git a/doc/examples/xor_ciph.cpp b/doc/examples/xor_ciph.cpp new file mode 100644 index 000000000..b57fbfc4d --- /dev/null +++ b/doc/examples/xor_ciph.cpp @@ -0,0 +1,95 @@ +/* + An implementation of the highly secure (not) XOR cipher. AKA, how to write + and use your own cipher object. DO NOT make up your own ciphers. Please. + + Written by Jack Lloyd ([email protected]) on Feb 17, 2004 + + This file is in the public domain +*/ +#include <botan/base.h> +#include <botan/init.h> +using namespace Botan; + +class XOR_Cipher : public StreamCipher + { + public: + // what we want to call this cipher + std::string name() const { return "XOR"; } + // return a new object of this type + StreamCipher* clone() const { return new XOR_Cipher; } + // StreamCipher() can take a number of args, which are: + // min keylen, max keylen, keylength mod, iv size + // In this case we just pass min keylen, which means the + // only keysize we support is 1 byte, and don't use an IV. + XOR_Cipher() : StreamCipher(1) { mask = 0; } + private: + void cipher(const byte[], byte[], u32bit); + void key(const byte[], u32bit); + byte mask; + }; + +void XOR_Cipher::cipher(const byte in[], byte out[], u32bit length) + { + for(u32bit j = 0; j != length; j++) + out[j] = in[j] ^ mask; + } + +void XOR_Cipher::key(const byte key[], u32bit) + { + /* We know length == 1 because it is checked in set_key against the + constraints we passed to StreamCipher's constructor. In this case, + we said: "All keys are of length 1 byte and no other length". + + An excercise for the reader would be to extend this to support + arbitrary length (for arbitrary in the range 1-32) keys. + */ + mask = key[0]; + } + +#include <fstream> +#include <iostream> +#include <string> +#include <vector> +#include <cstring> + +#include <botan/look_add.h> +#include <botan/lookup.h> +#include <botan/filters.h> + +int main() + { + LibraryInitializer init; + + add_algorithm(new XOR_Cipher); // make it available to use + add_alias("Vernam", "XOR"); // make Vernam an alias for XOR + + SymmetricKey key("42"); // a key of length 1, value hex 42 == dec 66 + + /* + Since stream ciphers are typically additive, the encryption and + decryption ops are the same, so this isn't terribly interesting. + + If this where a block cipher you would have to add a cipher mode and + padding method, such as "/CBC/PKCS7". + */ + Pipe enc(get_cipher("XOR", key, ENCRYPTION), new Hex_Encoder); + Pipe dec(new Hex_Decoder, get_cipher("Vernam", key, DECRYPTION)); + + // I think the pigeons are actually asleep at midnight... + std::string secret = "The pigeon flys at midnight."; + + std::cout << "The secret message is '" << secret << "'" << std::endl; + + enc.process_msg(secret); + std::string cipher = enc.read_all_as_string(); + + std::cout << "The encrypted secret message is " << cipher << std::endl; + + dec.process_msg(cipher); + secret = dec.read_all_as_string(); + + std::cout << "The decrypted secret message is '" + << secret << "'" << std::endl; + + return 0; + } |