aboutsummaryrefslogtreecommitdiffstats
path: root/misc
diff options
context:
space:
mode:
authorlloyd <[email protected]>2006-06-03 06:10:36 +0000
committerlloyd <[email protected]>2006-06-03 06:10:36 +0000
commit25aaeee23fd952525eadabea43b1c4e8a2cf1b02 (patch)
treef39ce2525b73734e289b4b591093c6b2436b7007 /misc
parent6f75c2fe6e536c0192f3acb00c84edbc762127da (diff)
Add the (very old) CMS | S/MIME support code to misc/cms
Diffstat (limited to 'misc')
-rw-r--r--misc/cms/cms_algo.cpp141
-rw-r--r--misc/cms/cms_comp.cpp98
-rw-r--r--misc/cms/cms_dalg.cpp267
-rw-r--r--misc/cms/cms_dec.cpp114
-rw-r--r--misc/cms/cms_dec.h63
-rw-r--r--misc/cms/cms_ealg.cpp369
-rw-r--r--misc/cms/cms_enc.cpp87
-rw-r--r--misc/cms/cms_enc.h70
-rw-r--r--misc/cms/readme.txt24
-rw-r--r--misc/cms/tests/cacert.pem15
-rw-r--r--misc/cms/tests/cms_dec.cpp111
-rw-r--r--misc/cms/tests/cms_enc.cpp46
-rw-r--r--misc/cms/tests/int_ca.pem18
-rw-r--r--misc/cms/tests/mycert.pem19
-rw-r--r--misc/cms/tests/mycert2.pem25
-rw-r--r--misc/cms/tests/mykey.pem17
-rw-r--r--misc/cms/tests/mykey2.pem11
-rw-r--r--misc/cms/tests/yourcert.pem18
18 files changed, 1513 insertions, 0 deletions
diff --git a/misc/cms/cms_algo.cpp b/misc/cms/cms_algo.cpp
new file mode 100644
index 000000000..8143bbe5c
--- /dev/null
+++ b/misc/cms/cms_algo.cpp
@@ -0,0 +1,141 @@
+/*************************************************
+* CMS Algorithm Specific Code Source File *
+* (C) 1999-2003 The Botan Project *
+*************************************************/
+
+#include <botan/cms_enc.h>
+#include <botan/der_enc.h>
+#include <botan/lookup.h>
+#include <botan/filters.h>
+#include <botan/rng.h>
+#include <botan/rc2.h>
+
+namespace Botan {
+
+namespace {
+
+/*************************************************
+* Wrap a key as specified in RFC 3217 *
+*************************************************/
+SecureVector<byte> do_rfc3217_wrap(const std::string& cipher,
+ const SymmetricKey& kek,
+ const SecureVector<byte>& input)
+ {
+ class Flip_Bytes : public Filter
+ {
+ public:
+ void write(const byte data[], u32bit length)
+ {
+ buf.append(data, length);
+ }
+ void end_msg()
+ {
+ for(u32bit j = 0; j != buf.size(); j++)
+ send(buf[buf.size()-j-1]);
+ buf.destroy();
+ }
+ Flip_Bytes(const SecureVector<byte>& prefix) { buf.append(prefix); }
+ private:
+ SecureVector<byte> buf;
+ };
+
+ if(block_size_of(cipher) != 8)
+ throw Encoding_Error("do_rfc3217_wrap: Bad cipher: " + cipher);
+
+ Pipe icv(new Hash_Filter("SHA-160", 8));
+ icv.process_msg(input);
+
+ InitializationVector iv(8);
+ InitializationVector fixed("4ADDA22C79E82105");
+
+ Pipe pipe(get_cipher(cipher + "/CBC/NoPadding", kek, iv, ENCRYPTION),
+ new Flip_Bytes(iv.copy()),
+ get_cipher(cipher + "/CBC/NoPadding", kek, fixed, ENCRYPTION));
+ pipe.start_msg();
+ pipe.write(input);
+ pipe.write(icv.read_all());
+ pipe.end_msg();
+ return pipe.read_all();
+ }
+
+}
+
+/*************************************************
+* Wrap a CEK with a KEK *
+*************************************************/
+SecureVector<byte> CMS_Encoder::wrap_key(const std::string& cipher,
+ const SymmetricKey& cek,
+ const SymmetricKey& kek)
+ {
+ if(cipher == "TripleDES")
+ {
+ SymmetricKey cek_parity = cek;
+ cek_parity.set_odd_parity();
+ return do_rfc3217_wrap(cipher, kek, cek_parity.copy());
+ }
+ else if(cipher == "RC2" || cipher == "CAST-128")
+ {
+ if(kek.length() != 16)
+ throw Encoding_Error("CMS: 128-bit KEKs must be used with " + cipher);
+
+ SecureVector<byte> lcekpad;
+ lcekpad.append((byte)cek.length());
+ lcekpad.append(cek.copy());
+ while(lcekpad.size() % 8)
+ lcekpad.append(Global_RNG::random(Nonce));
+ return do_rfc3217_wrap(cipher, kek, lcekpad);
+ }
+ else
+ throw Invalid_Argument("CMS_Encoder::wrap: Unknown cipher " + cipher);
+ }
+
+/*************************************************
+* Encode the parameters for an encryption algo *
+*************************************************/
+SecureVector<byte> CMS_Encoder::encode_params(const std::string& cipher,
+ const SymmetricKey& key,
+ const InitializationVector& iv)
+ {
+ DER_Encoder encoder;
+
+ if(cipher == "RC2")
+ {
+ encoder.start_sequence();
+ DER::encode(encoder, RC2::EKB_code(8*key.length()));
+ DER::encode(encoder, iv.copy(), OCTET_STRING);
+ encoder.end_sequence();
+ }
+ else if(cipher == "CAST-128")
+ {
+ encoder.start_sequence();
+ DER::encode(encoder, iv.copy(), OCTET_STRING);
+ DER::encode(encoder, 8*key.length());
+ encoder.end_sequence();
+ }
+ else
+ DER::encode(encoder, iv.copy(), OCTET_STRING);
+
+ return encoder.get_contents();
+ }
+
+/*************************************************
+* Generate a CEK or KEK for the cipher *
+*************************************************/
+SymmetricKey CMS_Encoder::setup_key(const std::string& cipher)
+ {
+ u32bit keysize = 0;
+
+ if(cipher == "TripleDES") keysize = 24;
+ if(cipher == "RC2") keysize = 16;
+ if(cipher == "CAST-128") keysize = 16;
+
+ if(keysize == 0)
+ throw Invalid_Argument("CMS: Cannot encrypt with cipher " + cipher);
+
+ SymmetricKey key(keysize);
+ if(cipher == "DES" || cipher == "TripleDES")
+ key.set_odd_parity();
+ return key;
+ }
+
+}
diff --git a/misc/cms/cms_comp.cpp b/misc/cms/cms_comp.cpp
new file mode 100644
index 000000000..8d120c172
--- /dev/null
+++ b/misc/cms/cms_comp.cpp
@@ -0,0 +1,98 @@
+/*************************************************
+* CMS Compression Source File *
+* (C) 1999-2003 The Botan Project *
+*************************************************/
+
+#include <botan/cms_enc.h>
+#include <botan/cms_dec.h>
+#include <botan/der_enc.h>
+#include <botan/ber_dec.h>
+#include <botan/oids.h>
+#include <botan/pipe.h>
+
+#if defined(BOTAN_EXT_COMPRESSOR_ZLIB)
+ #include <botan/zlib.h>
+ #define HAVE_ZLIB 1
+#else
+ #define HAVE_ZLIB 0
+#endif
+
+namespace Botan {
+
+/*************************************************
+* Compress a message *
+*************************************************/
+void CMS_Encoder::compress(const std::string& algo)
+ {
+ if(!CMS_Encoder::can_compress_with(algo))
+ throw Invalid_Argument("CMS_Encoder: Cannot compress with " + algo);
+
+ Filter* compressor = 0;
+
+#if HAVE_ZLIB
+ if(algo == "Zlib") compressor = new Zlib_Compression;
+#endif
+
+ if(compressor == 0)
+ throw Internal_Error("CMS: Couldn't get ahold of a compressor");
+
+ Pipe pipe(compressor);
+ pipe.process_msg(data);
+ SecureVector<byte> compressed = pipe.read_all();
+
+ DER_Encoder encoder;
+ encoder.start_sequence();
+ DER::encode(encoder, 0);
+ DER::encode(encoder, AlgorithmIdentifier("Compression." + algo, false));
+ encoder.add_raw_octets(make_econtent(compressed, type));
+ encoder.end_sequence();
+
+ add_layer("CMS.CompressedData", encoder);
+ }
+
+/*************************************************
+* See if the named compression algo is available *
+*************************************************/
+bool CMS_Encoder::can_compress_with(const std::string& algo)
+ {
+ if(HAVE_ZLIB && algo == "Zlib")
+ return true;
+ return false;
+ }
+
+/*************************************************
+* Decompress a message *
+*************************************************/
+void CMS_Decoder::decompress(BER_Decoder& decoder)
+ {
+ u32bit version;
+ AlgorithmIdentifier comp_algo;
+
+ BER_Decoder comp_info = BER::get_subsequence(decoder);
+ BER::decode(comp_info, version);
+ if(version != 0)
+ throw Decoding_Error("CMS: Unknown version for CompressedData");
+ BER::decode(comp_info, comp_algo);
+ read_econtent(comp_info);
+ comp_info.verify_end();
+
+ Filter* decompressor = 0;
+
+ info = comp_algo.oid.as_string();
+#if HAVE_ZLIB
+ if(comp_algo.oid == OIDS::lookup("Compression.Zlib"))
+ {
+ decompressor = new Zlib_Decompression;
+ info = "Zlib";
+ }
+#endif
+
+ if(!decompressor)
+ status = FAILURE;
+
+ Pipe pipe(decompressor);
+ pipe.process_msg(data);
+ data = pipe.read_all();
+ }
+
+}
diff --git a/misc/cms/cms_dalg.cpp b/misc/cms/cms_dalg.cpp
new file mode 100644
index 000000000..aed19e1b1
--- /dev/null
+++ b/misc/cms/cms_dalg.cpp
@@ -0,0 +1,267 @@
+/*************************************************
+* CMS Decoding Operations Source File *
+* (C) 1999-2003 The Botan Project *
+*************************************************/
+
+#include <botan/cms_dec.h>
+#include <botan/ber_dec.h>
+#include <botan/oids.h>
+#include <botan/lookup.h>
+#include <botan/look_pk.h>
+#include <memory>
+
+namespace Botan {
+
+namespace {
+
+/*************************************************
+* Compute the hash of some content *
+*************************************************/
+SecureVector<byte> hash_of(const SecureVector<byte>& content,
+ const AlgorithmIdentifier& hash_algo,
+ std::string& hash_name)
+ {
+ hash_name = OIDS::lookup(hash_algo.oid);
+ std::auto_ptr<HashFunction> hash_fn(get_hash(hash_name));
+ return hash_fn->process(content);
+ }
+
+/*************************************************
+* Find a cert based on SignerIdentifier *
+*************************************************/
+std::vector<X509_Certificate> get_cert(BER_Decoder& signer_info,
+ X509_Store& store)
+ {
+ BER_Object id = signer_info.get_next_object();
+
+ std::vector<X509_Certificate> found;
+
+ if(id.type_tag == SEQUENCE && id.class_tag == CONSTRUCTED)
+ {
+ X509_DN issuer;
+ BigInt serial;
+ BER_Decoder iands(id.value);
+ BER::decode(iands, issuer);
+ BER::decode(iands, serial);
+
+ found = X509_Store_Search::by_iands(store, issuer,
+ BigInt::encode(serial));
+ }
+ else if(id.type_tag == 0 && id.class_tag == CONSTRUCTED)
+ found = X509_Store_Search::by_SKID(store, id.value);
+ else
+ throw Decoding_Error("CMS: Unknown tag for cert identifier");
+
+ // verify cert if found
+
+ if(found.size() > 1)
+ throw Internal_Error("CMS: Found more than one match in get_cert");
+ return found;
+ }
+
+/*************************************************
+* Read OriginatorInfo *
+*************************************************/
+void read_orig_info(BER_Decoder& info, X509_Store& store)
+ {
+ BER_Object next = info.get_next_object();
+
+ if(next.type_tag == 0 &&
+ next.class_tag == ASN1_Tag(CONSTRUCTED | CONTEXT_SPECIFIC))
+ {
+ DataSource_Memory certs(next.value);
+ while(!certs.end_of_data())
+ {
+ // FIXME: can be attribute certs too
+ // FIXME: DoS?
+ X509_Certificate cert(certs);
+ store.add_cert(cert);
+ }
+ next = info.get_next_object();
+ }
+ if(next.type_tag == 1 &&
+ next.class_tag == ASN1_Tag(CONSTRUCTED | CONTEXT_SPECIFIC))
+ {
+ DataSource_Memory crls(next.value);
+ while(!crls.end_of_data())
+ {
+ // FIXME: DoS?
+ X509_CRL crl(crls);
+ store.add_crl(crl);
+ }
+ next = info.get_next_object();
+ }
+ info.push_back(next);
+ }
+
+/*************************************************
+* Decode any Attributes, and check type *
+*************************************************/
+SecureVector<byte> decode_attributes(BER_Decoder& ber, const OID& type,
+ bool& bad_attributes)
+ {
+ BER_Object obj = ber.get_next_object();
+ SecureVector<byte> digest;
+
+ bool got_digest = false;
+ bool got_content_type = false;
+
+ if(obj.type_tag == 0 &&
+ obj.class_tag == ASN1_Tag(CONSTRUCTED | CONTEXT_SPECIFIC))
+ ber.push_back(obj);
+ else
+ {
+ BER_Decoder attributes(obj.value);
+ while(attributes.more_items())
+ {
+ Attribute attr;
+ BER::decode(attributes, attr);
+ BER_Decoder attr_value(attr.parameters);
+
+ if(attr.oid == OIDS::lookup("PKCS9.MessageDigest"))
+ {
+ got_digest = true;
+ BER::decode(attr_value, digest, OCTET_STRING);
+ }
+ else if(attr.oid == OIDS::lookup("PKCS9.ContentType"))
+ {
+ got_content_type = true;
+ OID inner_type;
+ BER::decode(attr_value, inner_type);
+ if(inner_type != type)
+ bad_attributes = true;
+ }
+ else
+ throw Decoding_Error("Unknown/unhandled CMS attribute found: " +
+ OIDS::lookup(attr.oid));
+ }
+
+ if(!got_digest || !got_content_type)
+ bad_attributes = true;
+ }
+
+ return digest;
+ }
+
+}
+
+/*************************************************
+* Decode this layer of CMS encoding *
+*************************************************/
+void CMS_Decoder::decode_layer()
+ {
+ try {
+ if(status == FAILURE)
+ throw Invalid_State("CMS: Decoder is in FAILURE state");
+
+ status = GOOD;
+ info = "";
+
+ type = next_type;
+
+ if(type == OIDS::lookup("CMS.DataContent"))
+ return;
+
+ BER_Decoder decoder(data);
+ if(type == OIDS::lookup("CMS.CompressedData"))
+ decompress(decoder);
+ else if(type == OIDS::lookup("CMS.DigestedData"))
+ {
+ u32bit version;
+ AlgorithmIdentifier hash_algo;
+ SecureVector<byte> digest;
+
+ BER_Decoder hash_info = BER::get_subsequence(decoder);
+ BER::decode(hash_info, version);
+ if(version != 0 && version != 2)
+ throw Decoding_Error("CMS: Unknown version for DigestedData");
+ BER::decode(hash_info, hash_algo);
+ read_econtent(hash_info);
+ BER::decode(hash_info, digest, OCTET_STRING);
+ hash_info.verify_end();
+
+ if(digest != hash_of(data, hash_algo, info))
+ status = BAD;
+ }
+ else if(type == OIDS::lookup("CMS.SignedData"))
+ {
+#if 1
+ throw Exception("FIXME: not implemented");
+#else
+ u32bit version;
+
+ BER_Decoder sig_info = BER::get_subsequence(decoder);
+ BER::decode(sig_info, version);
+ if(version != 1 && version != 3)
+ throw Decoding_Error("CMS: Unknown version for SignedData");
+ BER::get_subset(sig_info); // hash algos (do something with these?)
+ read_econtent(sig_info);
+ read_orig_info(sig_info, store);
+
+ BER_Decoder signer_infos = BER::get_subset(sig_info);
+ while(signer_infos.more_items())
+ {
+ AlgorithmIdentifier sig_algo, hash_algo;
+ SecureVector<byte> signature, digest;
+ u32bit version;
+
+ BER_Decoder signer_info = BER::get_subsequence(signer_infos);
+ BER::decode(signer_info, version);
+ if(version != 1 && version != 3)
+ throw Decoding_Error("CMS: Unknown version for SignerInfo");
+
+ std::vector<X509_Certificate> certs = get_cert(signer_info, store);
+ if(certs.size() == 0) { status = NO_KEY; continue; }
+
+ BER::decode(signer_info, hash_algo);
+ bool bad_attr = false;
+ digest = decode_attributes(signer_info, next_type, bad_attr);
+ if(bad_attr) { status = BAD; continue; }
+ BER::decode(signer_info, sig_algo);
+ BER::decode(signer_info, signature, OCTET_STRING);
+ // unsigned attributes
+ signer_info.verify_end();
+
+ if(digest.has_items())
+ {
+ std::string hash;
+ if(digest != hash_of(data, hash_algo, hash))
+ {
+ status = BAD;
+ continue;
+ }
+ status = check_sig(signed_attr, sig_algo, signature, certs[0]);
+ }
+ else
+ status = check_sig(data, sig_algo, signature, certs[0]);
+
+ if(status == BAD)
+ continue;
+
+ // fix this (gets only last signer, for one thing)
+ // maybe some way for the user to get all certs that signed the
+ // message? that would be useful
+ info = "CN=" + cert.subject_info("CommonName") +
+ ",O=" + cert.subject_info("Organization") +
+ ",OU=" + cert.subject_info("Organizational Unit");
+ }
+#endif
+ }
+ else if(type == OIDS::lookup("CMS.EnvelopedData"))
+ {
+ throw Exception("FIXME: not implemented");
+ }
+ else if(type == OIDS::lookup("CMS.AuthenticatedData"))
+ {
+ throw Exception("FIXME: not implemented");
+ }
+ else
+ throw Decoding_Error("CMS: Unknown content ID " + type.as_string());
+ }
+ catch(std::exception)
+ {
+ status = FAILURE;
+ }
+ }
+
+}
diff --git a/misc/cms/cms_dec.cpp b/misc/cms/cms_dec.cpp
new file mode 100644
index 000000000..34ca82c19
--- /dev/null
+++ b/misc/cms/cms_dec.cpp
@@ -0,0 +1,114 @@
+/*************************************************
+* CMS Decoding Source File *
+* (C) 1999-2003 The Botan Project *
+*************************************************/
+
+#include <botan/cms_dec.h>
+#include <botan/ber_dec.h>
+#include <botan/oids.h>
+#include <botan/pem.h>
+
+namespace Botan {
+
+/*************************************************
+* CMS_Decoder Constructor *
+*************************************************/
+CMS_Decoder::CMS_Decoder(DataSource& in, const X509_Store& x509store,
+ User_Interface& ui_ref, PKCS8_PrivateKey* key) :
+ ui(ui_ref), store(x509store)
+ {
+ status = GOOD;
+
+ add_key(key);
+
+ if(BER::maybe_BER(in) && !PEM_Code::matches(in))
+ initial_read(in);
+ else
+ {
+ DataSource_Memory ber(PEM_Code::decode_check_label(in, "PKCS7"));
+ initial_read(ber);
+ }
+ }
+
+/*************************************************
+* Read the outermost ContentInfo *
+*************************************************/
+void CMS_Decoder::initial_read(DataSource& in)
+ {
+ BER_Decoder decoder(in);
+ BER_Decoder content_info = BER::get_subsequence(decoder);
+ BER::decode(content_info, next_type);
+ BER_Decoder content_type = BER::get_subsequence(content_info, ASN1_Tag(0));
+ data = content_type.get_remaining();
+
+ decode_layer();
+ }
+
+/*************************************************
+* Add another private key to use *
+*************************************************/
+void CMS_Decoder::add_key(PKCS8_PrivateKey* key)
+ {
+ if(!key)
+ return;
+
+ for(u32bit j = 0; j != keys.size(); j++)
+ if(keys[j]->key_id() == key->key_id())
+ return;
+
+ keys.push_back(key);
+ }
+
+/*************************************************
+* Return the status information *
+*************************************************/
+CMS_Decoder::Status CMS_Decoder::layer_status() const
+ {
+ return status;
+ }
+
+/*************************************************
+* Return the final data content *
+*************************************************/
+std::string CMS_Decoder::get_data() const
+ {
+ if(layer_type() != DATA)
+ throw Invalid_State("CMS: Cannot retrieve data from non-DATA layer");
+ return std::string((const char*)data.ptr(), data.size());
+ }
+
+/*************************************************
+* Return the content type of this layer *
+*************************************************/
+CMS_Decoder::Content_Type CMS_Decoder::layer_type() const
+ {
+ if(type == OIDS::lookup("CMS.DataContent")) return DATA;
+ if(type == OIDS::lookup("CMS.EnvelopedData")) return ENVELOPED;
+ if(type == OIDS::lookup("CMS.CompressedData")) return COMPRESSED;
+ if(type == OIDS::lookup("CMS.SignedData")) return SIGNED;
+ if(type == OIDS::lookup("CMS.AuthenticatedData")) return AUTHENTICATED;
+ if(type == OIDS::lookup("CMS.DigestedData")) return DIGESTED;
+ return UNKNOWN;
+ }
+
+/*************************************************
+* Return some information about this layer *
+*************************************************/
+std::string CMS_Decoder::layer_info() const
+ {
+ return info;
+ }
+
+/*************************************************
+* Return some information about this layer *
+*************************************************/
+void CMS_Decoder::read_econtent(BER_Decoder& decoder)
+ {
+ BER_Decoder econtent_info = BER::get_subsequence(decoder);
+ BER::decode(econtent_info, next_type);
+
+ BER_Decoder econtent = BER::get_subsequence(econtent_info, ASN1_Tag(0));
+ BER::decode(econtent, data, OCTET_STRING);
+ }
+
+}
diff --git a/misc/cms/cms_dec.h b/misc/cms/cms_dec.h
new file mode 100644
index 000000000..4aac92e58
--- /dev/null
+++ b/misc/cms/cms_dec.h
@@ -0,0 +1,63 @@
+/*************************************************
+* CMS Decoding Header File *
+* (C) 1999-2003 The Botan Project *
+*************************************************/
+
+#ifndef BOTAN_CMS_DECODER_H__
+#define BOTAN_CMS_DECODER_H__
+
+#include <botan/x509cert.h>
+#include <botan/x509stor.h>
+#include <botan/pkcs8.h>
+#include <botan/ber_dec.h>
+#include <botan/ui.h>
+
+namespace Botan {
+
+/*************************************************
+* CMS Decoding Operation *
+*************************************************/
+class CMS_Decoder
+ {
+ public:
+ enum Status { GOOD, BAD, NO_KEY, FAILURE };
+
+ enum Content_Type { DATA, UNKNOWN, COMPRESSED, ENVELOPED, SIGNED,
+ AUTHENTICATED, DIGESTED };
+
+ Status layer_status() const;
+ Content_Type layer_type() const;
+ std::string layer_info() const;
+ std::string layer_algo() const;
+ std::string get_data() const;
+ std::vector<X509_Certificate> get_certs() const;
+ std::vector<X509_CRL> get_crls() const;
+
+ void next_layer() { decode_layer(); }
+
+ void add_key(PKCS8_PrivateKey*);
+
+ CMS_Decoder(DataSource&, const X509_Store&, User_Interface&,
+ PKCS8_PrivateKey* = 0);
+ private:
+ std::string get_passphrase(const std::string&);
+ void read_econtent(BER_Decoder&);
+ void initial_read(DataSource&);
+ void decode_layer();
+ void decompress(BER_Decoder&);
+
+ User_Interface& ui;
+
+ X509_Store store;
+ std::vector<std::string> passphrases;
+ std::vector<PKCS8_PrivateKey*> keys;
+
+ OID type, next_type;
+ SecureVector<byte> data;
+ Status status;
+ std::string info;
+ };
+
+}
+
+#endif
diff --git a/misc/cms/cms_ealg.cpp b/misc/cms/cms_ealg.cpp
new file mode 100644
index 000000000..e576f8c68
--- /dev/null
+++ b/misc/cms/cms_ealg.cpp
@@ -0,0 +1,369 @@
+/*************************************************
+* CMS Encoding Operations Source File *
+* (C) 1999-2003 The Botan Project *
+*************************************************/
+
+#include <botan/cms_enc.h>
+#include <botan/der_enc.h>
+#include <botan/oids.h>
+#include <botan/lookup.h>
+#include <botan/look_pk.h>
+#include <botan/pipe.h>
+#include <botan/conf.h>
+
+namespace Botan {
+
+namespace {
+
+/*************************************************
+* Choose an algorithm *
+*************************************************/
+std::string choose_algo(const std::string& user_algo,
+ const std::string& default_algo)
+ {
+ if(user_algo == "")
+ return deref_alias(default_algo);
+ return deref_alias(user_algo);
+ }
+
+/*************************************************
+* Encode a SignerIdentifier/RecipientIdentifier *
+*************************************************/
+void encode_si(DER_Encoder& encoder, const X509_Certificate& cert,
+ bool use_skid_encoding = false)
+ {
+ if(cert.has_SKID() && use_skid_encoding)
+ DER::encode(encoder, cert.subject_key_id(), OCTET_STRING, ASN1_Tag(0));
+ else
+ {
+ encoder.start_sequence();
+ DER::encode(encoder, cert.issuer_dn());
+ DER::encode(encoder, cert.serial_number_bn());
+ encoder.end_sequence();
+ }
+ }
+
+/*************************************************
+* Compute the hash of some content *
+*************************************************/
+SecureVector<byte> hash_of(const SecureVector<byte>& content,
+ const std::string& hash)
+ {
+ std::auto_ptr<HashFunction> hash_fn(get_hash(hash));
+ return hash_fn->process(content);
+ }
+
+/*************************************************
+* Encode Attributes containing info on content *
+*************************************************/
+SecureVector<byte> encode_attr(const SecureVector<byte>& data,
+ const std::string& type,
+ const std::string& hash)
+ {
+ SecureVector<byte> digest = hash_of(data, hash);
+
+ DER_Encoder encoder;
+ DER::encode(encoder, OIDS::lookup(type));
+ Attribute content_type("PKCS9.ContentType", encoder.get_contents());
+
+ DER::encode(encoder, digest, OCTET_STRING);
+ Attribute message_digest("PKCS9.MessageDigest", encoder.get_contents());
+
+ encoder.start_set();
+ DER::encode(encoder, content_type);
+ DER::encode(encoder, message_digest);
+ encoder.end_set();
+
+ return encoder.get_contents();
+ }
+
+}
+
+/*************************************************
+* Encrypt a message *
+*************************************************/
+void CMS_Encoder::encrypt(const X509_Certificate& to,
+ const std::string user_cipher)
+ {
+ const std::string cipher = choose_algo(user_cipher, "TripleDES");
+
+ std::auto_ptr<X509_PublicKey> key(to.subject_public_key());
+ const std::string algo = key->algo_name();
+
+ Key_Constraints constraints = to.get_constraints();
+
+ if(algo == "RSA")
+ {
+ if(constraints != NO_CONSTRAINTS && !(constraints & KEY_ENCIPHERMENT))
+ throw Invalid_Argument("CMS: Constraints not set for encryption");
+
+ PK_Encrypting_Key* enc_key = dynamic_cast<PK_Encrypting_Key*>(key.get());
+ if(enc_key == 0)
+ throw Internal_Error("CMS_Encoder::encrypt: " + algo +
+ " can't encrypt");
+
+ encrypt_ktri(to, enc_key, cipher);
+ }
+ else if(algo == "DH")
+ {
+ if(constraints != NO_CONSTRAINTS && !(constraints & KEY_AGREEMENT))
+ throw Invalid_Argument("CMS: Constraints not set for key agreement");
+
+ encrypt_kari(to, key.get(), cipher);
+ }
+ else
+ throw Invalid_Argument("Unknown CMS PK encryption algorithm " + algo);
+ }
+
+/*************************************************
+* Encrypt a message with a key transport algo *
+*************************************************/
+void CMS_Encoder::encrypt_ktri(const X509_Certificate& to,
+ PK_Encrypting_Key* pub_key,
+ const std::string& cipher)
+ {
+ const std::string padding = "EME-PKCS1-v1_5";
+ const std::string pk_algo = pub_key->algo_name();
+ std::auto_ptr<PK_Encryptor> enc(get_pk_encryptor(*pub_key, padding));
+
+ SymmetricKey cek = setup_key(cipher);
+
+ DER_Encoder encoder;
+ encoder.start_sequence();
+ DER::encode(encoder, 0);
+ encoder.start_set();
+ encoder.start_sequence();
+ DER::encode(encoder, 0);
+ encode_si(encoder, to);
+ DER::encode(encoder, AlgorithmIdentifier(pk_algo + "/" + padding));
+ DER::encode(encoder, enc->encrypt(cek.copy()), OCTET_STRING);
+ encoder.end_sequence();
+ encoder.end_set();
+ encoder.add_raw_octets(do_encrypt(cek, cipher));
+ encoder.end_sequence();
+
+ add_layer("CMS.EnvelopedData", encoder);
+ }
+
+/*************************************************
+* Encrypt a message with a key agreement algo *
+*************************************************/
+void CMS_Encoder::encrypt_kari(const X509_Certificate&,
+ X509_PublicKey*,
+ const std::string&)
+ {
+ throw Exception("FIXME: unimplemented");
+#if 0
+ SymmetricKey cek = setup_key(cipher);
+
+ DER_Encoder encoder;
+ encoder.start_sequence();
+ DER::encode(encoder, 2);
+ encoder.start_set();
+ encoder.start_sequence(ASN1_Tag(1));
+ DER::encode(encoder, 3);
+ encode_si(encoder, to);
+ DER::encode(encoder, AlgorithmIdentifier(pk_algo + "/" + padding));
+ DER::encode(encoder, encrypted_cek, OCTET_STRING);
+ encoder.end_sequence(ASN1_Tag(1));
+ encoder.end_set();
+ encoder.add_raw_octets(do_encrypt(cek, cipher));
+ encoder.end_sequence();
+
+ add_layer("CMS.EnvelopedData", encoder);
+#endif
+ }
+
+/*************************************************
+* Encrypt a message with a shared key *
+*************************************************/
+void CMS_Encoder::encrypt(const SymmetricKey& kek,
+ const std::string& user_cipher)
+ {
+ throw Exception("FIXME: untested");
+
+ const std::string cipher = choose_algo(user_cipher, "TripleDES");
+ SymmetricKey cek = setup_key(cipher);
+
+ SecureVector<byte> kek_id; // FIXME: ?
+
+ DER_Encoder encoder;
+ encoder.start_sequence();
+ DER::encode(encoder, 2);
+ encoder.start_sequence(ASN1_Tag(2));
+ DER::encode(encoder, 4);
+ encoder.start_sequence();
+ DER::encode(encoder, kek_id, OCTET_STRING);
+ encoder.end_sequence();
+ DER::encode(encoder, AlgorithmIdentifier("KeyWrap." + cipher, true));
+ DER::encode(encoder, wrap_key(cipher, cek, kek), OCTET_STRING);
+ encoder.end_sequence(ASN1_Tag(2));
+ encoder.add_raw_octets(do_encrypt(cek, cipher));
+ encoder.end_sequence();
+
+ add_layer("CMS.EnvelopedData", encoder);
+ }
+
+/*************************************************
+* Encrypt a message with a passphrase *
+*************************************************/
+void CMS_Encoder::encrypt(const std::string&,
+ const std::string& user_cipher)
+ {
+ const std::string cipher = choose_algo(user_cipher, "TripleDES");
+ throw Exception("FIXME: unimplemented");
+ /*
+ SymmetricKey cek = setup_key(key);
+
+ DER_Encoder encoder;
+ encoder.start_sequence();
+ DER::encode(encoder, 0);
+ encoder.add_raw_octets(do_encrypt(cek, cipher));
+ encoder.end_sequence();
+
+ add_layer("CMS.EnvelopedData", encoder);
+ */
+ }
+
+/*************************************************
+* Encrypt the content with the chosen key/cipher *
+*************************************************/
+SecureVector<byte> CMS_Encoder::do_encrypt(const SymmetricKey& key,
+ const std::string& cipher)
+ {
+ if(!have_block_cipher(cipher))
+ throw Invalid_Argument("CMS: Can't encrypt with non-existent cipher " +
+ cipher);
+ if(!OIDS::have_oid(cipher + "/CBC"))
+ throw Encoding_Error("CMS: No OID assigned for " + cipher + "/CBC");
+
+ InitializationVector iv(block_size_of(cipher));
+
+ AlgorithmIdentifier content_cipher;
+ content_cipher.oid = OIDS::lookup(cipher + "/CBC");
+ content_cipher.parameters = encode_params(cipher, key, iv);
+
+ Pipe pipe(get_cipher(cipher + "/CBC/PKCS7", key, iv, ENCRYPTION));
+ pipe.process_msg(data);
+
+ DER_Encoder encoder;
+ encoder.start_sequence();
+ DER::encode(encoder, OIDS::lookup(type));
+ DER::encode(encoder, content_cipher);
+ DER::encode(encoder, pipe.read_all(), OCTET_STRING, ASN1_Tag(0));
+ encoder.end_sequence();
+
+ return encoder.get_contents();
+ }
+
+/*************************************************
+* Sign a message *
+*************************************************/
+void CMS_Encoder::sign(X509_Store& store, const PKCS8_PrivateKey& key)
+ {
+ std::vector<X509_Certificate> matching =
+ X509_Store_Search::by_keyid(store, key.key_id());
+ if(matching.size() == 0)
+ throw Encoding_Error("CMS::sign: Cannot find cert matching given key");
+
+ const X509_Certificate& cert = matching[0];
+
+ std::vector<X509_Certificate> chain = store.get_cert_chain(cert);
+
+ std::string padding, hash;
+ Signature_Format format;
+ Config::choose_sig_format(key.algo_name(), padding, hash, format);
+ const std::string sig_algo = key.algo_name() + "/" + padding;
+
+ SecureVector<byte> signed_attr = encode_attr(data, type, hash);
+ const PK_Signing_Key& sig_key = dynamic_cast<const PK_Signing_Key&>(key);
+ std::auto_ptr<PK_Signer> signer(get_pk_signer(sig_key, padding, format));
+ signer->update(signed_attr);
+ SecureVector<byte> signature = signer->signature();
+ signed_attr[0] = 0xA0;
+
+ const u32bit SI_VERSION = cert.has_SKID() ? 3 : 1;
+ const u32bit CMS_VERSION = (type != "CMS.DataContent") ? 3 : SI_VERSION;
+
+ DER_Encoder encoder;
+ encoder.start_sequence();
+ DER::encode(encoder, CMS_VERSION);
+ encoder.start_set();
+ DER::encode(encoder, AlgorithmIdentifier(hash, true));
+ encoder.end_set();
+ encoder.add_raw_octets(make_econtent(data, type));
+
+ encoder.start_set(ASN1_Tag(0));
+ for(u32bit j = 0; j != chain.size(); j++)
+ encoder.add_raw_octets(chain[j].BER_encode());
+ encoder.add_raw_octets(cert.BER_encode());
+ encoder.end_set(ASN1_Tag(0));
+
+ encoder.start_set();
+ encoder.start_sequence();
+ DER::encode(encoder, SI_VERSION);
+ encode_si(encoder, cert, ((SI_VERSION == 3) ? true : false));
+ DER::encode(encoder, AlgorithmIdentifier(hash, true));
+ encoder.add_raw_octets(signed_attr);
+ DER::encode(encoder, AlgorithmIdentifier(sig_algo, true));
+ DER::encode(encoder, signature, OCTET_STRING);
+ encoder.end_sequence();
+ encoder.end_set();
+ encoder.end_sequence();
+
+ add_layer("CMS.SignedData", encoder);
+ }
+
+/*************************************************
+* Digest a message *
+*************************************************/
+void CMS_Encoder::digest(const std::string& user_hash)
+ {
+ const std::string hash = choose_algo(user_hash, "SHA-1");
+ if(!OIDS::have_oid(hash))
+ throw Encoding_Error("CMS: No OID assigned for " + hash);
+
+ const u32bit VERSION = (type != "CMS.DataContent") ? 2 : 0;
+
+ DER_Encoder encoder;
+ encoder.start_sequence();
+ DER::encode(encoder, VERSION);
+ DER::encode(encoder, AlgorithmIdentifier(hash, true));
+ encoder.add_raw_octets(make_econtent(data, type));
+ DER::encode(encoder, hash_of(data, hash), OCTET_STRING);
+ encoder.end_sequence();
+
+ add_layer("CMS.DigestedData", encoder);
+ }
+
+/*************************************************
+* MAC a message with an encrypted key *
+*************************************************/
+void CMS_Encoder::authenticate(const X509_Certificate&,
+ const std::string& mac_algo)
+ {
+ const std::string mac = choose_algo(mac_algo, "HMAC(SHA-1)");
+ throw Exception("FIXME: unimplemented");
+ }
+
+/*************************************************
+* MAC a message with a shared key *
+*************************************************/
+void CMS_Encoder::authenticate(const SymmetricKey&,
+ const std::string& mac_algo)
+ {
+ const std::string mac = choose_algo(mac_algo, "HMAC(SHA-1)");
+ throw Exception("FIXME: unimplemented");
+ }
+
+/*************************************************
+* MAC a message with a passphrase *
+*************************************************/
+void CMS_Encoder::authenticate(const std::string&,
+ const std::string& mac_algo)
+ {
+ const std::string mac = choose_algo(mac_algo, "HMAC(SHA-1)");
+ throw Exception("FIXME: unimplemented");
+ }
+
+}
diff --git a/misc/cms/cms_enc.cpp b/misc/cms/cms_enc.cpp
new file mode 100644
index 000000000..58b3eadf6
--- /dev/null
+++ b/misc/cms/cms_enc.cpp
@@ -0,0 +1,87 @@
+/*************************************************
+* CMS Encoding Base Source File *
+* (C) 1999-2003 The Botan Project *
+*************************************************/
+
+#include <botan/cms_enc.h>
+#include <botan/der_enc.h>
+#include <botan/oids.h>
+#include <botan/pem.h>
+
+namespace Botan {
+
+/*************************************************
+* Setup the intitial layer of CMS data *
+*************************************************/
+void CMS_Encoder::set_data(const byte buf[], u32bit length)
+ {
+ if(data.has_items())
+ throw Invalid_State("Cannot call CMS_Encoder::set_data here");
+
+ data.set(buf, length);
+ type = "CMS.DataContent";
+ }
+
+/*************************************************
+* Setup the intitial layer of CMS data *
+*************************************************/
+void CMS_Encoder::set_data(const std::string& str)
+ {
+ set_data((const byte*)str.c_str(), str.length());
+ }
+
+/*************************************************
+* Finalize and return the CMS encoded data *
+*************************************************/
+SecureVector<byte> CMS_Encoder::get_contents()
+ {
+ DER_Encoder encoder;
+
+ encoder.start_sequence();
+ DER::encode(encoder, OIDS::lookup(type));
+ encoder.start_explicit(ASN1_Tag(0));
+ encoder.add_raw_octets(data);
+ encoder.end_explicit(ASN1_Tag(0));
+ encoder.end_sequence();
+
+ data.clear();
+
+ return encoder.get_contents();
+ }
+
+/*************************************************
+* Add a new layer of encapsulation *
+*************************************************/
+void CMS_Encoder::add_layer(const std::string& oid, DER_Encoder& new_layer)
+ {
+ data = new_layer.get_contents();
+ type = oid;
+ }
+
+/*************************************************
+* Return the PEM-encoded data *
+*************************************************/
+std::string CMS_Encoder::PEM_contents()
+ {
+ return PEM_Code::encode(get_contents(), "PKCS7");
+ }
+
+/*************************************************
+* Make an EncapsulatedContentInfo *
+*************************************************/
+SecureVector<byte> CMS_Encoder::make_econtent(const SecureVector<byte>& data,
+ const std::string& type)
+ {
+ DER_Encoder encoder;
+
+ encoder.start_sequence();
+ DER::encode(encoder, OIDS::lookup(type));
+ encoder.start_explicit(ASN1_Tag(0));
+ DER::encode(encoder, data, OCTET_STRING);
+ encoder.end_explicit(ASN1_Tag(0));
+ encoder.end_sequence();
+
+ return encoder.get_contents();
+ }
+
+}
diff --git a/misc/cms/cms_enc.h b/misc/cms/cms_enc.h
new file mode 100644
index 000000000..ac0634985
--- /dev/null
+++ b/misc/cms/cms_enc.h
@@ -0,0 +1,70 @@
+/*************************************************
+* CMS Encoding Header File *
+* (C) 1999-2003 The Botan Project *
+*************************************************/
+
+#ifndef BOTAN_CMS_ENCODER_H__
+#define BOTAN_CMS_ENCODER_H__
+
+#include <botan/x509cert.h>
+#include <botan/x509stor.h>
+#include <botan/pkcs8.h>
+
+namespace Botan {
+
+/*************************************************
+* CMS Encoding Operation *
+*************************************************/
+class CMS_Encoder
+ {
+ public:
+ void encrypt(const X509_Certificate&, const std::string = "");
+ void encrypt(const std::string&, const std::string& = "");
+ void encrypt(const SymmetricKey&, const std::string& = "");
+
+ void authenticate(const X509_Certificate&, const std::string& = "");
+ void authenticate(const std::string&, const std::string& = "");
+ void authenticate(const SymmetricKey&, const std::string& = "");
+
+ void sign(X509_Store&, const PKCS8_PrivateKey&);
+ void digest(const std::string& = "");
+
+ void compress(const std::string&);
+ static bool can_compress_with(const std::string&);
+
+ SecureVector<byte> get_contents();
+ std::string PEM_contents();
+
+ void set_data(const std::string&);
+ void set_data(const byte[], u32bit);
+
+ CMS_Encoder(const std::string& str) { set_data(str); }
+ CMS_Encoder(const byte buf[], u32bit length) { set_data(buf, length); }
+ private:
+ void add_layer(const std::string&, DER_Encoder&);
+
+ void encrypt_ktri(const X509_Certificate&, PK_Encrypting_Key*,
+ const std::string&);
+ void encrypt_kari(const X509_Certificate&, X509_PublicKey*,
+ const std::string&);
+
+ SecureVector<byte> do_encrypt(const SymmetricKey&, const std::string&);
+
+ static SecureVector<byte> make_econtent(const SecureVector<byte>&,
+ const std::string&);
+
+ static SymmetricKey setup_key(const std::string&);
+ static SecureVector<byte> wrap_key(const std::string&,
+ const SymmetricKey&,
+ const SymmetricKey&);
+ static SecureVector<byte> encode_params(const std::string&,
+ const SymmetricKey&,
+ const InitializationVector&);
+
+ SecureVector<byte> data;
+ std::string type;
+ };
+
+}
+
+#endif
diff --git a/misc/cms/readme.txt b/misc/cms/readme.txt
new file mode 100644
index 000000000..b4a0afad0
--- /dev/null
+++ b/misc/cms/readme.txt
@@ -0,0 +1,24 @@
+Botan CMS alpha1
+December 12, 2003
+
+To use this, download a recent version of Botan (1.2.8 or 1.3.7 are best), and
+move the source files to src/, the headers to include/ and then run
+configure/make as normal. You will then have a CMS encoder/decoder. There are a
+couple of testing apps in testing/ which should show the general idea. Lots of
+it is unimplemented (which it will tell you by throwing exceptions), but basic
+RSA encryption/signatures and a few other things work OK. The encoder is much
+more complete than the decoder.
+
+The encoder works by initializing it with some bits, then calling some number
+of operations on it (compress(), digest(), encrypt(), etc) which successfully
+'envelope' the data. The decoder works exactly the same, except backwards. (Of
+course!)
+
+This was supposed to have been finished by now, but it's kind of stalled. I
+want CMS support, I just don't really want to code it. So I've been occupying
+myself with various distractions. I'll probably finish it *someday*.
+
+If someone out there really wants this soon, and is willing to pay some nominal
+consulting fee (we're talking cheap), let me know and we can talk about
+it. I think I need some external motivation of some sort to force me to tell
+with the mess that is CMS.
diff --git a/misc/cms/tests/cacert.pem b/misc/cms/tests/cacert.pem
new file mode 100644
index 000000000..70263abe0
--- /dev/null
+++ b/misc/cms/tests/cacert.pem
@@ -0,0 +1,15 @@
+-----BEGIN CERTIFICATE-----
+MIICYjCCAcugAwIBAgIRAMccNerZcWvudgzuyzcOVjgwDQYJKoZIhvcNAQEFBQAw
+PTELMAkGA1UEBhMCUFIxEjAQBgNVBAoTCVByaW9uQ29ycDEaMBgGA1UEAxMRUHJp
+b25Db3JwIFJvb3QgQ0EwHhcNMDMxMTA5MDEyNjA0WhcNMDQxMTA4MDEyNjM0WjA9
+MQswCQYDVQQGEwJQUjESMBAGA1UEChMJUHJpb25Db3JwMRowGAYDVQQDExFQcmlv
+bkNvcnAgUm9vdCBDQTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAtXP7pUMt
+iL/Vbz9keSw7V/UClsVoCnybHNoHt1hU8WXGInUeZdBmZdhga/N/ugH0LvvFyERT
+63oJz92vbv7sSYjueaPFVP2g+CIJfZPsVsy5G91YyYUFxrevCkVB21VxcRqlflo8
+DokB3t58rJ4+OKkR9aqs/QMT8MS7z3J/FzkCAwEAAaNiMGAwHQYDVR0OBBYEFHL3
+F37cN9LKEouhUYfWMLDzsk92MBIGA1UdEwEB/wQIMAYBAf8CAQgwGwYDVR0RBBQw
+EoEQY2FAcHJpb25jb3JwLmNvbTAOBgNVHQ8BAf8EBAMCAQYwDQYJKoZIhvcNAQEF
+BQADgYEAZ+NKT1Y6ciyM2IInE7IXw+npYhzA22HsTk+vxJVOpBt+9wAucqpKxiVs
+SbYtwabP/4DoKnurHRojoKTdkGGDkco+9Vmlt/9eiTa8WqIeiWnnPV4k1VeWdGA0
+nei/Ds0KG5crGtqmX8DUjz9r5E43pnEMR2T7LuDg/Ko/WZissh0=
+-----END CERTIFICATE-----
diff --git a/misc/cms/tests/cms_dec.cpp b/misc/cms/tests/cms_dec.cpp
new file mode 100644
index 000000000..593cf2e09
--- /dev/null
+++ b/misc/cms/tests/cms_dec.cpp
@@ -0,0 +1,111 @@
+#include <botan/cms_dec.h>
+using namespace Botan;
+
+#include <iostream>
+
+int main(int argc, char* argv[])
+ {
+ if(argc != 2)
+ {
+ printf("Usage: %s <filename>\n", argv[0]);
+ return 1;
+ }
+
+ LibraryInitializer init;
+
+ try {
+ X509_Certificate mycert("mycert.pem");
+ PKCS8_PrivateKey* mykey = PKCS8::load_key("mykey.pem", "cut");
+
+ X509_Certificate yourcert("yourcert.pem");
+ X509_Certificate cacert("cacert.pem");
+ X509_Certificate int_ca("int_ca.pem");
+
+ X509_Store store;
+ store.add_cert(mycert);
+ store.add_cert(yourcert);
+ store.add_cert(cacert, true);
+ store.add_cert(int_ca);
+
+ DataSource_Stream message(argv[1]);
+
+ User_Interface ui;
+
+ CMS_Decoder decoder(message, store, ui, mykey);
+
+ while(decoder.layer_type() != CMS_Decoder::DATA)
+ {
+ CMS_Decoder::Status status = decoder.layer_status();
+ CMS_Decoder::Content_Type content = decoder.layer_type();
+
+ if(status == CMS_Decoder::FAILURE)
+ {
+ std::cout << "Failure reading CMS data" << std::endl;
+ break;
+ }
+
+ if(content == CMS_Decoder::DIGESTED)
+ {
+ std::cout << "Digested data, hash = " << decoder.layer_info()
+ << std::endl;
+ std::cout << "Hash is "
+ << ((status == CMS_Decoder::GOOD) ? "good" : "bad")
+ << std::endl;
+ }
+
+ if(content == CMS_Decoder::SIGNED)
+ {
+ // how to handle multiple signers? they can all exist within a
+ // single level...
+
+ std::cout << "Signed by " << decoder.layer_info() << std::endl;
+ //std::cout << "Sign time: " << decoder.xxx() << std::endl;
+ std::cout << "Signature is ";
+ if(status == CMS_Decoder::GOOD)
+ std::cout << "valid";
+ else if(status == CMS_Decoder::BAD)
+ std::cout << "bad";
+ else if(status == CMS_Decoder::NO_KEY)
+ std::cout << "(cannot check, no known cert)";
+ std::cout << std::endl;
+ }
+ if(content == CMS_Decoder::ENVELOPED ||
+ content == CMS_Decoder::COMPRESSED ||
+ content == CMS_Decoder::AUTHENTICATED)
+ {
+ if(content == CMS_Decoder::ENVELOPED)
+ std::cout << "Enveloped";
+ if(content == CMS_Decoder::COMPRESSED)
+ std::cout << "Compressed";
+ if(content == CMS_Decoder::AUTHENTICATED)
+ std::cout << "MACed";
+
+ std::cout << ", algo = " << decoder.layer_info() << std::endl;
+
+ if(content == CMS_Decoder::AUTHENTICATED)
+ {
+ std::cout << "MAC status is ";
+ if(status == CMS_Decoder::GOOD)
+ std::cout << "valid";
+ else if(status == CMS_Decoder::BAD)
+ std::cout << "bad";
+ else if(status == CMS_Decoder::NO_KEY)
+ std::cout << "(cannot check, no key)";
+ std::cout << std::endl;
+ }
+ }
+ decoder.next_layer();
+ }
+
+ if(decoder.layer_type() == CMS_Decoder::DATA)
+ std::cout << "Message is \"" << decoder.get_data()
+ << '"' << std::endl;
+ else
+ std::cout << "No data anywhere?" << std::endl;
+ }
+ catch(std::exception& e)
+ {
+ std::cerr << e.what() << std::endl;
+ }
+ return 0;
+ }
diff --git a/misc/cms/tests/cms_enc.cpp b/misc/cms/tests/cms_enc.cpp
new file mode 100644
index 000000000..0319925d8
--- /dev/null
+++ b/misc/cms/tests/cms_enc.cpp
@@ -0,0 +1,46 @@
+#include <botan/cms_enc.h>
+using namespace Botan;
+
+#include <iostream>
+#include <fstream>
+
+int main()
+ {
+ LibraryInitializer init;
+
+ try {
+ PKCS8_PrivateKey* mykey = PKCS8::load_key("mykey.pem", "cut");
+
+ X509_Certificate mycert("mycert.pem");
+ X509_Certificate mycert2("mycert2.pem");
+ X509_Certificate yourcert("yourcert.pem");
+ X509_Certificate cacert("cacert.pem");
+ X509_Certificate int_ca("int_ca.pem");
+
+ X509_Store store;
+ store.add_cert(mycert);
+ store.add_cert(mycert2);
+ store.add_cert(yourcert);
+ store.add_cert(int_ca);
+ store.add_cert(cacert, true);
+
+ const std::string msg = "prioncorp: we don't toy\n";
+
+ CMS_Encoder encoder(msg);
+
+ encoder.compress("Zlib");
+ encoder.digest();
+ encoder.encrypt(mycert);
+ encoder.sign(store, *mykey);
+
+ SecureVector<byte> raw = encoder.get_contents();
+ std::ofstream out("out.der");
+
+ out.write((const char*)raw.ptr(), raw.size());
+ }
+ catch(std::exception& e)
+ {
+ std::cerr << e.what() << std::endl;
+ }
+ return 0;
+ }
diff --git a/misc/cms/tests/int_ca.pem b/misc/cms/tests/int_ca.pem
new file mode 100644
index 000000000..85e35ab88
--- /dev/null
+++ b/misc/cms/tests/int_ca.pem
@@ -0,0 +1,18 @@
+-----BEGIN CERTIFICATE-----
+MIIC3zCCAkigAwIBAgIRAIfaYqkyhxP/BUSIiqx9/bUwDQYJKoZIhvcNAQEFBQAw
+PTELMAkGA1UEBhMCUFIxEjAQBgNVBAoTCVByaW9uQ29ycDEaMBgGA1UEAxMRUHJp
+b25Db3JwIFJvb3QgQ0EwHhcNMDMxMTA5MDEzMjA1WhcNMDQxMTA4MDEzMjA1WjBu
+MQswCQYDVQQGEwJVUzELMAkGA1UECBMCTUQxEjAQBgNVBAcTCUJhbHRpbW9yZTES
+MBAGA1UEChMJUHJpb25Db3JwMRAwDgYDVQQLEwdUZXN0aW5nMRgwFgYDVQQDEw9Q
+cmlvbiBQZXJzb25uZWwwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAL2cw+jh
+K17j+SBFJqC2oaiPW70T1chtJ1EOtG+SovZXgmLk29fVJCWq1YuihGAOXJUBVGgS
+1/IO2WhE3DaU2ekMiiXCQd1cVBLuhrg2aGzOGUifrvvgaLYUHRvctnuYBh/hm04+
+GFpWMeIaly9ZH+yImd6Td7n1yrzsq7Evl/+lAgMBAAGjga0wgaowHQYDVR0OBBYE
+FEbeVBJJPVESjB0ZEYaorZU/WxVlMB8GA1UdIwQYMBaAFHL3F37cN9LKEouhUYfW
+MLDzsk92MBIGA1UdEwEB/wQIMAYBAf8CAQgwGwYDVR0RBBQwEoEQcHBAcHJpb25j
+b3JwLmNvbTAOBgNVHQ8BAf8EBAMCAQYwJwYDVR0lBCAwHgYIKwYBBQUHAwIGCCsG
+AQUFBwMHBggrBgEFBQcDBDANBgkqhkiG9w0BAQUFAAOBgQAOBYopIXBr8rq95GW3
+l5I/8IKuUq0/jBzW9766uv1GJUS37CcsQ0isdDrBASZfae4nqWUTIRbEVZ6s0yNb
+fauzzoPv13EwnxZECUKADJpLaYgH43v6oil47ljFuuhANQMMFAQ8kTwhUkM4F0eQ
+VGBoTBmoIV2Vppf/MRIpQIq0pA==
+-----END CERTIFICATE-----
diff --git a/misc/cms/tests/mycert.pem b/misc/cms/tests/mycert.pem
new file mode 100644
index 000000000..05082b3bf
--- /dev/null
+++ b/misc/cms/tests/mycert.pem
@@ -0,0 +1,19 @@
+-----BEGIN CERTIFICATE-----
+MIIDAzCCAmygAwIBAgIRAOVlsPFarxiOCqAgatU2fpgwDQYJKoZIhvcNAQEFBQAw
+bjELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAk1EMRIwEAYDVQQHEwlCYWx0aW1vcmUx
+EjAQBgNVBAoTCVByaW9uQ29ycDEQMA4GA1UECxMHVGVzdGluZzEYMBYGA1UEAxMP
+UHJpb24gUGVyc29ubmVsMB4XDTAzMTEwOTAxMzMzN1oXDTA0MTEwODAxMzMzN1ow
+ZDELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAk1EMRIwEAYDVQQHEwlCYWx0aW1vcmUx
+EjAQBgNVBAoTCVByaW9uQ29ycDEQMA4GA1UECxMHVGVzdGluZzEOMAwGA1UEAxMF
+R1JPSUQwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAKf3JbJymfnnelEy4HrK
+0wcwOCO6F6W1u98VV01RKEJtff/0m2QVVPRuHXtofZOWwwxZ9e4X2mB+Q1DQjkZM
+Q9d/sPvNtIL47QVRNOc9EnfaoGbohlTT+dONj8eYWQ1PU5FtADAEXmXwTWsCk5eO
+arpISIyda3bMycwx4hGkwcxHAgMBAAGjgaowgacwHQYDVR0OBBYEFFQgVRRLqxb8
+FJ+s7OQB++dmXdVmMB8GA1UdIwQYMBaAFEbeVBJJPVESjB0ZEYaorZU/WxVlMAwG
+A1UdEwEB/wQCMAAwHgYDVR0RBBcwFYETZ3JvaWRAcHJpb25jb3JwLmNvbTAOBgNV
+HQ8BAf8EBAMCBeAwJwYDVR0lBCAwHgYIKwYBBQUHAwIGCCsGAQUFBwMHBggrBgEF
+BQcDBDANBgkqhkiG9w0BAQUFAAOBgQA2g/iN3cQUDqG5yEzoTtmvDlOkwRruYLOn
+8/M7znnPk28l1QVtIy+mCgbFP5khNEHo3ogSjVI83FlRFDTx4mXV81hwK+xi/eLW
+MeNfJdHxqRuMMbY9/a2To90jS0NRsCCI917QlT0iOVBpoe0i28Is7ofRZ0MKtZMY
+cxcB6XWeug==
+-----END CERTIFICATE-----
diff --git a/misc/cms/tests/mycert2.pem b/misc/cms/tests/mycert2.pem
new file mode 100644
index 000000000..d7efb31ea
--- /dev/null
+++ b/misc/cms/tests/mycert2.pem
@@ -0,0 +1,25 @@
+-----BEGIN CERTIFICATE-----
+MIIEHTCCA4agAwIBAgIRANP/9If4mMQ1Y+EIPrzxWg0wDQYJKoZIhvcNAQEFBQAw
+bjELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAk1EMRIwEAYDVQQHEwlCYWx0aW1vcmUx
+EjAQBgNVBAoTCVByaW9uQ29ycDEQMA4GA1UECxMHVGVzdGluZzEYMBYGA1UEAxMP
+UHJpb24gUGVyc29ubmVsMB4XDTAzMTExMjA0MzUxNFoXDTA0MTExMTA0MzUxNFow
+ZDELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAk1EMRIwEAYDVQQHEwlCYWx0aW1vcmUx
+EjAQBgNVBAoTCVByaW9uQ29ycDEQMA4GA1UECxMHVGVzdGluZzEOMAwGA1UEAxMF
+R1JPSUQwggG3MIIBKwYHKoZIzjgEATCCAR4CgYEA/X9TgR11EilS30qcLuzk5/YR
+t1I870QAwx4/gLZRJmlFXUAiUftZPY1Y+r/F9bow9subVWzXgTuAHTRv8mZgt2uZ
+UKWkn5/oBHsQIsJPu6nX/rfGG/g7V+fGqKYVDwT7g/bTxR7DAjVUE1oWkTL2dfOu
+K2HXKu/yIgMZndFIAccCFQCXYFCPFSMLzLKSuYKi64QL8Fgc9QKBgEaWA1EuMCeM
+05R1ldsi7smCamMirclzRPQddAwyVyTI+e+6p9TYA/+MYJ3NEA68W9/PrXxqQl+u
+p4bqIFDr6YNR6h/aH98k1pR6prmqI3ZpU4AvTX1KjsugbRl2iiSR/7FtDvnEOpm1
+9xZy/28KJLRE0HNtBNOKGhMi2vbN2IydA4GFAAKBgQDolXHX/FpKLzYJYlYXxS29
++Fvug5lma45gnX68o5v+v5lrZgXJ3o6AP1WL9Y5rWrCwCNFMJ/60s9XxBPBXyD4H
+bQp0nDFrtkigMrC/Ue/btjzLThKrGhnWNh0EYw/BURsbbaOnxzi8hpIc1n8qXDuK
+hlkxUUvovfHj2E4G7IzKdaOBqzCBqDAdBgNVHQ4EFgQU84gmHCKCv04yWRXHbQPq
+SX0fzBQwHwYDVR0jBBgwFoAURt5UEkk9URKMHRkRhqitlT9bFWUwDAYDVR0TAQH/
+BAIwADAfBgNVHREEGDAWgRRncm9pZEBicnV0YWx1bml0LmNvbTAOBgNVHQ8BAf8E
+BAMCBsAwJwYDVR0lBCAwHgYIKwYBBQUHAwIGCCsGAQUFBwMHBggrBgEFBQcDBDAN
+BgkqhkiG9w0BAQUFAAOBgQC4YptXYHJIs/mTaTnTKNSPYSEN8sYfuS5r6pRPbxsM
+8uusP2vPgzAVCnQpg27/m9SdcAyZb+yiVCRZ5Kk5+CIKWfWNJDmcBxFJlL+UL7x0
+Qor1+B/IlmPNOZHlXDkpEFA6A9UC8aGYwA9DJmcdMdXxaQy2b1d9en0WyxBRQMnY
+6A==
+-----END CERTIFICATE-----
diff --git a/misc/cms/tests/mykey.pem b/misc/cms/tests/mykey.pem
new file mode 100644
index 000000000..46e94e140
--- /dev/null
+++ b/misc/cms/tests/mykey.pem
@@ -0,0 +1,17 @@
+-----BEGIN ENCRYPTED PRIVATE KEY-----
+MIICyTBDBgkqhkiG9w0BBQ0wNjAeBgkqhkiG9w0BBQwwEQQI34hFoRijoOICAggA
+AgEYMBQGCCqGSIb3DQMHBAgTmcMqYk8EZQSCAoAPeiGKaCNgcbfoTV/Pxc9TducM
+cAMdHvETAVQVR9k7dGqmbtkaMH+0tVwJwtDhn9/bEvf6xC1zwlyIQ7b4J94/IMf5
+17IIVGMn1P2f46ASvw+Y9BPD9JPUh96Ohkya5SHX+UiRWR0EGQhfzKFcO8YC2Sl6
++7itEw9boojYDFRt2W3hEEGURZGZiMH7dOlRCLzF6F+qOzNs79RwBWTaWK5TCu0N
+Vdo8ulqLEo63Wxc6dN5utr6RSBOFP1wKLjQd+lBmW7T/PuwKwUch6UzTdNwVuSKP
+3JJXdZykCxrDRIKevwyotaVU+7XWV+WrLgcli95N3eKpeWgVr+qT/HPjTkCFPi0p
+1iLLht56kilRRMsizV0LlQjdzqt5Sketp8kAqsEV/AjwrUfDP0QF/iCaVZ0Z7i3O
+71RrlW2a3LDUUTLRS6/4e28ATuSIxb5fTJktmK102Q8GdH0dNIfrkUBK6xgthzEN
+FGPrYGdbxSHxR6k8Q7hMKmYEqmUSo27nOfKbOJlbwS6xGtpAH8dC/bb1JjnyYGfR
+0kqVHGkTQqsMqBLKjNvzYXvEX1BokqXD+KafunWmHhMeQzyOYRgagPsKalMMFz53
+EP7EgMCjhkC222wEbyAPWCjuKZ2OOZnqHr/3MSFOVUQIELFNcPpJcf7+C/hGQjV9
+OWaSWSFa6eQGvNJsBXbQQCBSY8FvCliQcn2e3PG98RPC6FHbSgjsW10Z8WNiWKOG
+F5MM7Pov4GIEcT/6b8DhRkNwB9PWlcnVR2jUgEX5UcB2tgljHshR3gKC9LnLQn+B
+xB+uTrsnd4G9dCkhg2NmgZ0ghJ2katupqbwpADd13kXGXCVLjfuksYxTX1S1
+-----END ENCRYPTED PRIVATE KEY-----
diff --git a/misc/cms/tests/mykey2.pem b/misc/cms/tests/mykey2.pem
new file mode 100644
index 000000000..69cc76111
--- /dev/null
+++ b/misc/cms/tests/mykey2.pem
@@ -0,0 +1,11 @@
+-----BEGIN ENCRYPTED PRIVATE KEY-----
+MIIBmTBDBgkqhkiG9w0BBQ0wNjAeBgkqhkiG9w0BBQwwEQQI3mP0zRQGjQgCAggA
+AgEYMBQGCCqGSIb3DQMHBAiwp1QHcRxj0ASCAVCmM0QKIyYDS3iggiZjG300eV1N
+MxFKSfre39BuMNZhd0hIA5T7F+Do84d1dwhK8wdxq5yLRq9pcHaLCYm0iPdtPzVO
+OIunUD/3CudNHu6rdBE+rtfd8rLuzHnkgTJJY5wxIGaZdu7O7J8fuBPt5/gTF+an
++rO4U4ZJiFvCCfQzjQHyFUinVAxTE9OozguhcIj3TMnZQwSpXw8HYVVNO+WAN1n1
++6moNYbKmd69hrKnmg3uxmV7hPBTeFssrdlny3G9yCb5bIPZ/URpv0sM/HLkH2Gl
+3v0m50okhnkNIgUgQ45sg2/u6cyoMqxuP7te4jPWalWY0FFMt/Kx8O0eL9eZwcK3
+Vs0QYt6MzCRVRPsrdHFczOHT5Ygk3MW0ZP8uLNJjhDPVYCaJprIuk19wco0z8a7x
+734n8uHqlpzOUE7LpGcmaIOeEe7fIr4ZnIfw7QU=
+-----END ENCRYPTED PRIVATE KEY-----
diff --git a/misc/cms/tests/yourcert.pem b/misc/cms/tests/yourcert.pem
new file mode 100644
index 000000000..bea147354
--- /dev/null
+++ b/misc/cms/tests/yourcert.pem
@@ -0,0 +1,18 @@
+-----BEGIN CERTIFICATE-----
+MIIC1zCCAkCgAwIBAgIRAOe/xWvUYwSm0TOMqoPF2u0wDQYJKoZIhvcNAQEFBQAw
+PTELMAkGA1UEBhMCUFIxEjAQBgNVBAoTCVByaW9uQ29ycDEaMBgGA1UEAxMRUHJp
+b25Db3JwIFJvb3QgQ0EwHhcNMDMxMTA5MDEyOTExWhcNMDQxMTA4MDEyOTExWjBn
+MQswCQYDVQQGEwJSVTELMAkGA1UECBMCTUQxEjAQBgNVBAcTCUJhbHRpbW9yZTES
+MBAGA1UEChMJUHJpb25Db3JwMRAwDgYDVQQLEwdUZXN0aW5nMREwDwYDVQQDEwgy
+OTdCMDU4MzCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA3Td7nAtgyM4nxiYX
+wRMqyJ39XkHnR4YIMjK9nHr4adIbJcRD5xRRylIUweQiLjc3nTn5uDjXggRCbcV8
+l7aJP20S9Rm5Z5HOYH1UH2KqEmF1JIXj/0DCfl51InFOpKhlVUdXy3hB18Pp9y4X
+wnvl3xblumNIlyUeFv6klA1vQQUCAwEAAaOBrDCBqTAdBgNVHQ4EFgQUP10rBSlh
+YKuDOLTKlgXPzXGavcUwHwYDVR0jBBgwFoAUcvcXftw30soSi6FRh9YwsPOyT3Yw
+DAYDVR0TAQH/BAIwADAgBgNVHREEGTAXgRUyOTdCMDU4M0BwcmlvbmNvcnAucnUw
+DgYDVR0PAQH/BAQDAgXgMCcGA1UdJQQgMB4GCCsGAQUFBwMCBggrBgEFBQcDBwYI
+KwYBBQUHAwQwDQYJKoZIhvcNAQEFBQADgYEAlZJaF89dOIfocPHEvsKuU58LwjPC
+FGivwcMdU9z4/bDNvYlfk7YnC6Nv8MsJWjv5B0deDZF8CbS0RHzmfAh5zwpY5tDS
+r26zAEzW0jxJ6EfvL546G5OcIr7L2nRgMBkHGdWsxmkxilzt/dk9tPAJoQd181BA
+Zg6t51mR+rHfzJQ=
+-----END CERTIFICATE-----